michael@0: /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: package org.mozilla.gecko.gfx; michael@0: michael@0: import org.mozilla.gecko.util.FloatUtils; michael@0: michael@0: import android.graphics.PointF; michael@0: import android.graphics.Rect; michael@0: import android.graphics.RectF; michael@0: michael@0: import java.nio.FloatBuffer; michael@0: import java.util.concurrent.locks.ReentrantLock; michael@0: michael@0: public abstract class Layer { michael@0: private final ReentrantLock mTransactionLock; michael@0: private boolean mInTransaction; michael@0: private Rect mNewPosition; michael@0: private float mNewResolution; michael@0: michael@0: protected Rect mPosition; michael@0: protected float mResolution; michael@0: michael@0: public Layer() { michael@0: this(null); michael@0: } michael@0: michael@0: public Layer(IntSize size) { michael@0: mTransactionLock = new ReentrantLock(); michael@0: if (size == null) { michael@0: mPosition = new Rect(); michael@0: } else { michael@0: mPosition = new Rect(0, 0, size.width, size.height); michael@0: } michael@0: mResolution = 1.0f; michael@0: } michael@0: michael@0: /** michael@0: * Updates the layer. This returns false if there is still work to be done michael@0: * after this update. michael@0: */ michael@0: public final boolean update(RenderContext context) { michael@0: if (mTransactionLock.isHeldByCurrentThread()) { michael@0: throw new RuntimeException("draw() called while transaction lock held by this " + michael@0: "thread?!"); michael@0: } michael@0: michael@0: if (mTransactionLock.tryLock()) { michael@0: try { michael@0: performUpdates(context); michael@0: return true; michael@0: } finally { michael@0: mTransactionLock.unlock(); michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: /** Subclasses override this function to draw the layer. */ michael@0: public abstract void draw(RenderContext context); michael@0: michael@0: /** Given the intrinsic size of the layer, returns the pixel boundaries of the layer rect. */ michael@0: protected RectF getBounds(RenderContext context) { michael@0: return RectUtils.scale(new RectF(mPosition), context.zoomFactor / mResolution); michael@0: } michael@0: michael@0: /** michael@0: * Call this before modifying the layer. Note that, for TileLayers, "modifying the layer" michael@0: * includes altering the underlying CairoImage in any way. Thus you must call this function michael@0: * before modifying the byte buffer associated with this layer. michael@0: * michael@0: * This function may block, so you should never call this on the main UI thread. michael@0: */ michael@0: public void beginTransaction() { michael@0: if (mTransactionLock.isHeldByCurrentThread()) michael@0: throw new RuntimeException("Nested transactions are not supported"); michael@0: mTransactionLock.lock(); michael@0: mInTransaction = true; michael@0: mNewResolution = mResolution; michael@0: } michael@0: michael@0: /** Call this when you're done modifying the layer. */ michael@0: public void endTransaction() { michael@0: if (!mInTransaction) michael@0: throw new RuntimeException("endTransaction() called outside a transaction"); michael@0: mInTransaction = false; michael@0: mTransactionLock.unlock(); michael@0: } michael@0: michael@0: /** Returns true if the layer is currently in a transaction and false otherwise. */ michael@0: protected boolean inTransaction() { michael@0: return mInTransaction; michael@0: } michael@0: michael@0: /** Returns the current layer position. */ michael@0: public Rect getPosition() { michael@0: return mPosition; michael@0: } michael@0: michael@0: /** Sets the position. Only valid inside a transaction. */ michael@0: public void setPosition(Rect newPosition) { michael@0: if (!mInTransaction) michael@0: throw new RuntimeException("setPosition() is only valid inside a transaction"); michael@0: mNewPosition = newPosition; michael@0: } michael@0: michael@0: /** Returns the current layer's resolution. */ michael@0: public float getResolution() { michael@0: return mResolution; michael@0: } michael@0: michael@0: /** michael@0: * Sets the layer resolution. This value is used to determine how many pixels per michael@0: * device pixel this layer was rendered at. This will be reflected by scaling by michael@0: * the reciprocal of the resolution in the layer's transform() function. michael@0: * Only valid inside a transaction. */ michael@0: public void setResolution(float newResolution) { michael@0: if (!mInTransaction) michael@0: throw new RuntimeException("setResolution() is only valid inside a transaction"); michael@0: mNewResolution = newResolution; michael@0: } michael@0: michael@0: /** michael@0: * Subclasses may override this method to perform custom layer updates. This will be called michael@0: * with the transaction lock held. Subclass implementations of this method must call the michael@0: * superclass implementation. Returns false if there is still work to be done after this michael@0: * update is complete. michael@0: */ michael@0: protected void performUpdates(RenderContext context) { michael@0: if (mNewPosition != null) { michael@0: mPosition = mNewPosition; michael@0: mNewPosition = null; michael@0: } michael@0: if (mNewResolution != 0.0f) { michael@0: mResolution = mNewResolution; michael@0: mNewResolution = 0.0f; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * This function fills in the provided dest array with values to render a texture. michael@0: * The array is filled with 4 sets of {x, y, z, texture_x, texture_y} values (so 20 values michael@0: * in total) corresponding to the corners of the rect. michael@0: */ michael@0: protected final void fillRectCoordBuffer(float[] dest, RectF rect, float viewWidth, float viewHeight, michael@0: Rect cropRect, float texWidth, float texHeight) { michael@0: //x, y, z, texture_x, texture_y michael@0: dest[0] = rect.left / viewWidth; michael@0: dest[1] = rect.bottom / viewHeight; michael@0: dest[2] = 0; michael@0: dest[3] = cropRect.left / texWidth; michael@0: dest[4] = cropRect.top / texHeight; michael@0: michael@0: dest[5] = rect.left / viewWidth; michael@0: dest[6] = rect.top / viewHeight; michael@0: dest[7] = 0; michael@0: dest[8] = cropRect.left / texWidth; michael@0: dest[9] = cropRect.bottom / texHeight; michael@0: michael@0: dest[10] = rect.right / viewWidth; michael@0: dest[11] = rect.bottom / viewHeight; michael@0: dest[12] = 0; michael@0: dest[13] = cropRect.right / texWidth; michael@0: dest[14] = cropRect.top / texHeight; michael@0: michael@0: dest[15] = rect.right / viewWidth; michael@0: dest[16] = rect.top / viewHeight; michael@0: dest[17] = 0; michael@0: dest[18] = cropRect.right / texWidth; michael@0: dest[19] = cropRect.bottom / texHeight; michael@0: } michael@0: michael@0: public static class RenderContext { michael@0: public final RectF viewport; michael@0: public final RectF pageRect; michael@0: public final float zoomFactor; michael@0: public final PointF offset; michael@0: public final int positionHandle; michael@0: public final int textureHandle; michael@0: public final FloatBuffer coordBuffer; michael@0: michael@0: public RenderContext(RectF aViewport, RectF aPageRect, float aZoomFactor, PointF aOffset, michael@0: int aPositionHandle, int aTextureHandle, FloatBuffer aCoordBuffer) { michael@0: viewport = aViewport; michael@0: pageRect = aPageRect; michael@0: zoomFactor = aZoomFactor; michael@0: offset = aOffset; michael@0: positionHandle = aPositionHandle; michael@0: textureHandle = aTextureHandle; michael@0: coordBuffer = aCoordBuffer; michael@0: } michael@0: michael@0: public boolean fuzzyEquals(RenderContext other) { michael@0: if (other == null) { michael@0: return false; michael@0: } michael@0: return RectUtils.fuzzyEquals(viewport, other.viewport) michael@0: && RectUtils.fuzzyEquals(pageRect, other.pageRect) michael@0: && FloatUtils.fuzzyEquals(zoomFactor, other.zoomFactor) michael@0: && FloatUtils.fuzzyEquals(offset, other.offset); michael@0: } michael@0: } michael@0: } michael@0: