1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
2  * This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 package org.mozilla.gecko.gfx;
7 
8 import android.graphics.Rect;
9 import android.graphics.RectF;
10 import android.graphics.Region;
11 
12 import org.mozilla.gecko.util.FloatUtils;
13 
14 import java.nio.FloatBuffer;
15 import java.util.concurrent.locks.ReentrantLock;
16 
17 public abstract class Layer {
18     private final ReentrantLock mTransactionLock;
19     private boolean mInTransaction;
20     private Rect mNewPosition;
21     private float mNewResolution;
22 
23     protected Rect mPosition;
24     protected float mResolution;
25     protected boolean mUsesDefaultProgram = true;
26 
Layer()27     public Layer() {
28         this(null);
29     }
30 
Layer(IntSize size)31     public Layer(IntSize size) {
32         mTransactionLock = new ReentrantLock();
33         if (size == null) {
34             mPosition = new Rect();
35         } else {
36             mPosition = new Rect(0, 0, size.width, size.height);
37         }
38         mResolution = 1.0f;
39     }
40 
41     /**
42      * Updates the layer. This returns false if there is still work to be done
43      * after this update.
44      */
update(RenderContext context)45     public final boolean update(RenderContext context) {
46         if (mTransactionLock.isHeldByCurrentThread()) {
47             throw new RuntimeException("draw() called while transaction lock held by this " +
48                                        "thread?!");
49         }
50 
51         if (mTransactionLock.tryLock()) {
52             try {
53                 performUpdates(context);
54                 return true;
55             } finally {
56                 mTransactionLock.unlock();
57             }
58         }
59 
60         return false;
61     }
62 
63     /** Subclasses override this function to draw the layer. */
draw(RenderContext context)64     public abstract void draw(RenderContext context);
65 
66     /** Given the intrinsic size of the layer, returns the pixel boundaries of the layer rect. */
getBounds(RenderContext context)67     protected RectF getBounds(RenderContext context) {
68         return RectUtils.scale(new RectF(mPosition), context.zoomFactor / mResolution);
69     }
70 
71     /**
72      * Returns the region of the layer that is considered valid. The default
73      * implementation of this will return the bounds of the layer, but this
74      * may be overridden.
75      */
getValidRegion(RenderContext context)76     public Region getValidRegion(RenderContext context) {
77         return new Region(RectUtils.round(getBounds(context)));
78     }
79 
80     /**
81      * Call this before modifying the layer. Note that, for TileLayers, "modifying the layer"
82      * includes altering the underlying CairoImage in any way. Thus you must call this function
83      * before modifying the byte buffer associated with this layer.
84      *
85      * This function may block, so you should never call this on the main UI thread.
86      */
beginTransaction()87     public void beginTransaction() {
88         if (mTransactionLock.isHeldByCurrentThread())
89             throw new RuntimeException("Nested transactions are not supported");
90         mTransactionLock.lock();
91         mInTransaction = true;
92         mNewResolution = mResolution;
93     }
94 
95     /** Call this when you're done modifying the layer. */
endTransaction()96     public void endTransaction() {
97         if (!mInTransaction)
98             throw new RuntimeException("endTransaction() called outside a transaction");
99         mInTransaction = false;
100         mTransactionLock.unlock();
101     }
102 
103     /** Returns true if the layer is currently in a transaction and false otherwise. */
inTransaction()104     protected boolean inTransaction() {
105         return mInTransaction;
106     }
107 
108     /** Returns the current layer position. */
getPosition()109     public Rect getPosition() {
110         return mPosition;
111     }
112 
113     /** Sets the position. Only valid inside a transaction. */
setPosition(Rect newPosition)114     public void setPosition(Rect newPosition) {
115         if (!mInTransaction)
116             throw new RuntimeException("setPosition() is only valid inside a transaction");
117         mNewPosition = newPosition;
118     }
119 
120     /** Returns the current layer's resolution. */
getResolution()121     public float getResolution() {
122         return mResolution;
123     }
124 
125     /**
126      * Sets the layer resolution. This value is used to determine how many pixels per
127      * device pixel this layer was rendered at. This will be reflected by scaling by
128      * the reciprocal of the resolution in the layer's transform() function.
129      * Only valid inside a transaction. */
setResolution(float newResolution)130     public void setResolution(float newResolution) {
131         if (!mInTransaction)
132             throw new RuntimeException("setResolution() is only valid inside a transaction");
133         mNewResolution = newResolution;
134     }
135 
usesDefaultProgram()136     public boolean usesDefaultProgram() {
137         return mUsesDefaultProgram;
138     }
139 
140     /**
141      * Subclasses may override this method to perform custom layer updates. This will be called
142      * with the transaction lock held. Subclass implementations of this method must call the
143      * superclass implementation. Returns false if there is still work to be done after this
144      * update is complete.
145      */
performUpdates(RenderContext context)146     protected void performUpdates(RenderContext context) {
147         if (mNewPosition != null) {
148             mPosition = mNewPosition;
149             mNewPosition = null;
150         }
151         if (mNewResolution != 0.0f) {
152             mResolution = mNewResolution;
153             mNewResolution = 0.0f;
154         }
155     }
156 
157     /**
158      * This function fills in the provided <tt>dest</tt> array with values to render a texture.
159      * The array is filled with 4 sets of {x, y, z, texture_x, texture_y} values (so 20 values
160      * in total) corresponding to the corners of the rect.
161      */
fillRectCoordBuffer(float[] dest, RectF rect, float viewWidth, float viewHeight, Rect cropRect, float texWidth, float texHeight)162     protected final void fillRectCoordBuffer(float[] dest, RectF rect, float viewWidth, float viewHeight,
163                                              Rect cropRect, float texWidth, float texHeight) {
164         //x, y, z, texture_x, texture_y
165         dest[0] = rect.left / viewWidth;
166         dest[1] = rect.bottom / viewHeight;
167         dest[2] = 0;
168         dest[3] = cropRect.left / texWidth;
169         dest[4] = cropRect.top / texHeight;
170 
171         dest[5] = rect.left / viewWidth;
172         dest[6] = rect.top / viewHeight;
173         dest[7] = 0;
174         dest[8] = cropRect.left / texWidth;
175         dest[9] = cropRect.bottom / texHeight;
176 
177         dest[10] = rect.right / viewWidth;
178         dest[11] = rect.bottom / viewHeight;
179         dest[12] = 0;
180         dest[13] = cropRect.right / texWidth;
181         dest[14] = cropRect.top / texHeight;
182 
183         dest[15] = rect.right / viewWidth;
184         dest[16] = rect.top / viewHeight;
185         dest[17] = 0;
186         dest[18] = cropRect.right / texWidth;
187         dest[19] = cropRect.bottom / texHeight;
188     }
189 
190     public static class RenderContext {
191         public final RectF viewport;
192         public final RectF pageRect;
193         public final float zoomFactor;
194         public final int positionHandle;
195         public final int textureHandle;
196         public final FloatBuffer coordBuffer;
197 
RenderContext(RectF aViewport, RectF aPageRect, float aZoomFactor, int aPositionHandle, int aTextureHandle, FloatBuffer aCoordBuffer)198         public RenderContext(RectF aViewport, RectF aPageRect, float aZoomFactor,
199                              int aPositionHandle, int aTextureHandle, FloatBuffer aCoordBuffer) {
200             viewport = aViewport;
201             pageRect = aPageRect;
202             zoomFactor = aZoomFactor;
203             positionHandle = aPositionHandle;
204             textureHandle = aTextureHandle;
205             coordBuffer = aCoordBuffer;
206         }
207 
fuzzyEquals(RenderContext other)208         public boolean fuzzyEquals(RenderContext other) {
209             if (other == null) {
210                 return false;
211             }
212             return RectUtils.fuzzyEquals(viewport, other.viewport)
213                 && RectUtils.fuzzyEquals(pageRect, other.pageRect)
214                 && FloatUtils.fuzzyEquals(zoomFactor, other.zoomFactor);
215         }
216     }
217 }
218 
219