1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/base/gfx/Layer.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,207 @@ 1.4 +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +package org.mozilla.gecko.gfx; 1.10 + 1.11 +import org.mozilla.gecko.util.FloatUtils; 1.12 + 1.13 +import android.graphics.PointF; 1.14 +import android.graphics.Rect; 1.15 +import android.graphics.RectF; 1.16 + 1.17 +import java.nio.FloatBuffer; 1.18 +import java.util.concurrent.locks.ReentrantLock; 1.19 + 1.20 +public abstract class Layer { 1.21 + private final ReentrantLock mTransactionLock; 1.22 + private boolean mInTransaction; 1.23 + private Rect mNewPosition; 1.24 + private float mNewResolution; 1.25 + 1.26 + protected Rect mPosition; 1.27 + protected float mResolution; 1.28 + 1.29 + public Layer() { 1.30 + this(null); 1.31 + } 1.32 + 1.33 + public Layer(IntSize size) { 1.34 + mTransactionLock = new ReentrantLock(); 1.35 + if (size == null) { 1.36 + mPosition = new Rect(); 1.37 + } else { 1.38 + mPosition = new Rect(0, 0, size.width, size.height); 1.39 + } 1.40 + mResolution = 1.0f; 1.41 + } 1.42 + 1.43 + /** 1.44 + * Updates the layer. This returns false if there is still work to be done 1.45 + * after this update. 1.46 + */ 1.47 + public final boolean update(RenderContext context) { 1.48 + if (mTransactionLock.isHeldByCurrentThread()) { 1.49 + throw new RuntimeException("draw() called while transaction lock held by this " + 1.50 + "thread?!"); 1.51 + } 1.52 + 1.53 + if (mTransactionLock.tryLock()) { 1.54 + try { 1.55 + performUpdates(context); 1.56 + return true; 1.57 + } finally { 1.58 + mTransactionLock.unlock(); 1.59 + } 1.60 + } 1.61 + 1.62 + return false; 1.63 + } 1.64 + 1.65 + /** Subclasses override this function to draw the layer. */ 1.66 + public abstract void draw(RenderContext context); 1.67 + 1.68 + /** Given the intrinsic size of the layer, returns the pixel boundaries of the layer rect. */ 1.69 + protected RectF getBounds(RenderContext context) { 1.70 + return RectUtils.scale(new RectF(mPosition), context.zoomFactor / mResolution); 1.71 + } 1.72 + 1.73 + /** 1.74 + * Call this before modifying the layer. Note that, for TileLayers, "modifying the layer" 1.75 + * includes altering the underlying CairoImage in any way. Thus you must call this function 1.76 + * before modifying the byte buffer associated with this layer. 1.77 + * 1.78 + * This function may block, so you should never call this on the main UI thread. 1.79 + */ 1.80 + public void beginTransaction() { 1.81 + if (mTransactionLock.isHeldByCurrentThread()) 1.82 + throw new RuntimeException("Nested transactions are not supported"); 1.83 + mTransactionLock.lock(); 1.84 + mInTransaction = true; 1.85 + mNewResolution = mResolution; 1.86 + } 1.87 + 1.88 + /** Call this when you're done modifying the layer. */ 1.89 + public void endTransaction() { 1.90 + if (!mInTransaction) 1.91 + throw new RuntimeException("endTransaction() called outside a transaction"); 1.92 + mInTransaction = false; 1.93 + mTransactionLock.unlock(); 1.94 + } 1.95 + 1.96 + /** Returns true if the layer is currently in a transaction and false otherwise. */ 1.97 + protected boolean inTransaction() { 1.98 + return mInTransaction; 1.99 + } 1.100 + 1.101 + /** Returns the current layer position. */ 1.102 + public Rect getPosition() { 1.103 + return mPosition; 1.104 + } 1.105 + 1.106 + /** Sets the position. Only valid inside a transaction. */ 1.107 + public void setPosition(Rect newPosition) { 1.108 + if (!mInTransaction) 1.109 + throw new RuntimeException("setPosition() is only valid inside a transaction"); 1.110 + mNewPosition = newPosition; 1.111 + } 1.112 + 1.113 + /** Returns the current layer's resolution. */ 1.114 + public float getResolution() { 1.115 + return mResolution; 1.116 + } 1.117 + 1.118 + /** 1.119 + * Sets the layer resolution. This value is used to determine how many pixels per 1.120 + * device pixel this layer was rendered at. This will be reflected by scaling by 1.121 + * the reciprocal of the resolution in the layer's transform() function. 1.122 + * Only valid inside a transaction. */ 1.123 + public void setResolution(float newResolution) { 1.124 + if (!mInTransaction) 1.125 + throw new RuntimeException("setResolution() is only valid inside a transaction"); 1.126 + mNewResolution = newResolution; 1.127 + } 1.128 + 1.129 + /** 1.130 + * Subclasses may override this method to perform custom layer updates. This will be called 1.131 + * with the transaction lock held. Subclass implementations of this method must call the 1.132 + * superclass implementation. Returns false if there is still work to be done after this 1.133 + * update is complete. 1.134 + */ 1.135 + protected void performUpdates(RenderContext context) { 1.136 + if (mNewPosition != null) { 1.137 + mPosition = mNewPosition; 1.138 + mNewPosition = null; 1.139 + } 1.140 + if (mNewResolution != 0.0f) { 1.141 + mResolution = mNewResolution; 1.142 + mNewResolution = 0.0f; 1.143 + } 1.144 + } 1.145 + 1.146 + /** 1.147 + * This function fills in the provided <tt>dest</tt> array with values to render a texture. 1.148 + * The array is filled with 4 sets of {x, y, z, texture_x, texture_y} values (so 20 values 1.149 + * in total) corresponding to the corners of the rect. 1.150 + */ 1.151 + protected final void fillRectCoordBuffer(float[] dest, RectF rect, float viewWidth, float viewHeight, 1.152 + Rect cropRect, float texWidth, float texHeight) { 1.153 + //x, y, z, texture_x, texture_y 1.154 + dest[0] = rect.left / viewWidth; 1.155 + dest[1] = rect.bottom / viewHeight; 1.156 + dest[2] = 0; 1.157 + dest[3] = cropRect.left / texWidth; 1.158 + dest[4] = cropRect.top / texHeight; 1.159 + 1.160 + dest[5] = rect.left / viewWidth; 1.161 + dest[6] = rect.top / viewHeight; 1.162 + dest[7] = 0; 1.163 + dest[8] = cropRect.left / texWidth; 1.164 + dest[9] = cropRect.bottom / texHeight; 1.165 + 1.166 + dest[10] = rect.right / viewWidth; 1.167 + dest[11] = rect.bottom / viewHeight; 1.168 + dest[12] = 0; 1.169 + dest[13] = cropRect.right / texWidth; 1.170 + dest[14] = cropRect.top / texHeight; 1.171 + 1.172 + dest[15] = rect.right / viewWidth; 1.173 + dest[16] = rect.top / viewHeight; 1.174 + dest[17] = 0; 1.175 + dest[18] = cropRect.right / texWidth; 1.176 + dest[19] = cropRect.bottom / texHeight; 1.177 + } 1.178 + 1.179 + public static class RenderContext { 1.180 + public final RectF viewport; 1.181 + public final RectF pageRect; 1.182 + public final float zoomFactor; 1.183 + public final PointF offset; 1.184 + public final int positionHandle; 1.185 + public final int textureHandle; 1.186 + public final FloatBuffer coordBuffer; 1.187 + 1.188 + public RenderContext(RectF aViewport, RectF aPageRect, float aZoomFactor, PointF aOffset, 1.189 + int aPositionHandle, int aTextureHandle, FloatBuffer aCoordBuffer) { 1.190 + viewport = aViewport; 1.191 + pageRect = aPageRect; 1.192 + zoomFactor = aZoomFactor; 1.193 + offset = aOffset; 1.194 + positionHandle = aPositionHandle; 1.195 + textureHandle = aTextureHandle; 1.196 + coordBuffer = aCoordBuffer; 1.197 + } 1.198 + 1.199 + public boolean fuzzyEquals(RenderContext other) { 1.200 + if (other == null) { 1.201 + return false; 1.202 + } 1.203 + return RectUtils.fuzzyEquals(viewport, other.viewport) 1.204 + && RectUtils.fuzzyEquals(pageRect, other.pageRect) 1.205 + && FloatUtils.fuzzyEquals(zoomFactor, other.zoomFactor) 1.206 + && FloatUtils.fuzzyEquals(offset, other.offset); 1.207 + } 1.208 + } 1.209 +} 1.210 +