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