Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
michael@0 | 1 | /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- |
michael@0 | 2 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | package org.mozilla.gecko.gfx; |
michael@0 | 7 | |
michael@0 | 8 | import android.graphics.Rect; |
michael@0 | 9 | import android.graphics.RectF; |
michael@0 | 10 | import android.graphics.Region; |
michael@0 | 11 | import android.graphics.RegionIterator; |
michael@0 | 12 | import android.opengl.GLES20; |
michael@0 | 13 | |
michael@0 | 14 | import java.nio.FloatBuffer; |
michael@0 | 15 | |
michael@0 | 16 | /** |
michael@0 | 17 | * Encapsulates the logic needed to draw a single textured tile. |
michael@0 | 18 | * |
michael@0 | 19 | * TODO: Repeating textures really should be their own type of layer. |
michael@0 | 20 | */ |
michael@0 | 21 | public class SingleTileLayer extends TileLayer { |
michael@0 | 22 | private static final String LOGTAG = "GeckoSingleTileLayer"; |
michael@0 | 23 | |
michael@0 | 24 | private Rect mMask; |
michael@0 | 25 | |
michael@0 | 26 | // To avoid excessive GC, declare some objects here that would otherwise |
michael@0 | 27 | // be created and destroyed frequently during draw(). |
michael@0 | 28 | private final RectF mBounds; |
michael@0 | 29 | private final RectF mTextureBounds; |
michael@0 | 30 | private final RectF mViewport; |
michael@0 | 31 | private final Rect mIntBounds; |
michael@0 | 32 | private final Rect mSubRect; |
michael@0 | 33 | private final RectF mSubRectF; |
michael@0 | 34 | private final Region mMaskedBounds; |
michael@0 | 35 | private final Rect mCropRect; |
michael@0 | 36 | private final RectF mObjRectF; |
michael@0 | 37 | private final float[] mCoords; |
michael@0 | 38 | |
michael@0 | 39 | public SingleTileLayer(CairoImage image) { |
michael@0 | 40 | this(false, image); |
michael@0 | 41 | } |
michael@0 | 42 | |
michael@0 | 43 | public SingleTileLayer(boolean repeat, CairoImage image) { |
michael@0 | 44 | this(image, repeat ? TileLayer.PaintMode.REPEAT : TileLayer.PaintMode.NORMAL); |
michael@0 | 45 | } |
michael@0 | 46 | |
michael@0 | 47 | public SingleTileLayer(CairoImage image, TileLayer.PaintMode paintMode) { |
michael@0 | 48 | super(image, paintMode); |
michael@0 | 49 | |
michael@0 | 50 | mBounds = new RectF(); |
michael@0 | 51 | mTextureBounds = new RectF(); |
michael@0 | 52 | mViewport = new RectF(); |
michael@0 | 53 | mIntBounds = new Rect(); |
michael@0 | 54 | mSubRect = new Rect(); |
michael@0 | 55 | mSubRectF = new RectF(); |
michael@0 | 56 | mMaskedBounds = new Region(); |
michael@0 | 57 | mCropRect = new Rect(); |
michael@0 | 58 | mObjRectF = new RectF(); |
michael@0 | 59 | mCoords = new float[20]; |
michael@0 | 60 | } |
michael@0 | 61 | |
michael@0 | 62 | /** |
michael@0 | 63 | * Set an area to mask out when rendering. |
michael@0 | 64 | */ |
michael@0 | 65 | public void setMask(Rect aMaskRect) { |
michael@0 | 66 | mMask = aMaskRect; |
michael@0 | 67 | } |
michael@0 | 68 | |
michael@0 | 69 | @Override |
michael@0 | 70 | public void draw(RenderContext context) { |
michael@0 | 71 | // mTextureIDs may be null here during startup if Layer.java's draw method |
michael@0 | 72 | // failed to acquire the transaction lock and call performUpdates. |
michael@0 | 73 | if (!initialized()) |
michael@0 | 74 | return; |
michael@0 | 75 | |
michael@0 | 76 | mViewport.set(context.viewport); |
michael@0 | 77 | |
michael@0 | 78 | if (repeats()) { |
michael@0 | 79 | // If we're repeating, we want to adjust the texture bounds so that |
michael@0 | 80 | // the texture repeats the correct number of times when drawn at |
michael@0 | 81 | // the size of the viewport. |
michael@0 | 82 | mBounds.set(getBounds(context)); |
michael@0 | 83 | mTextureBounds.set(0.0f, 0.0f, mBounds.width(), mBounds.height()); |
michael@0 | 84 | mBounds.set(0.0f, 0.0f, mViewport.width(), mViewport.height()); |
michael@0 | 85 | } else if (stretches()) { |
michael@0 | 86 | // If we're stretching, we just want the bounds and texture bounds |
michael@0 | 87 | // to fit to the page. |
michael@0 | 88 | mBounds.set(context.pageRect); |
michael@0 | 89 | mTextureBounds.set(mBounds); |
michael@0 | 90 | } else { |
michael@0 | 91 | mBounds.set(getBounds(context)); |
michael@0 | 92 | mTextureBounds.set(mBounds); |
michael@0 | 93 | } |
michael@0 | 94 | |
michael@0 | 95 | mBounds.roundOut(mIntBounds); |
michael@0 | 96 | mMaskedBounds.set(mIntBounds); |
michael@0 | 97 | if (mMask != null) { |
michael@0 | 98 | mMaskedBounds.op(mMask, Region.Op.DIFFERENCE); |
michael@0 | 99 | if (mMaskedBounds.isEmpty()) |
michael@0 | 100 | return; |
michael@0 | 101 | } |
michael@0 | 102 | |
michael@0 | 103 | // XXX Possible optimisation here, form this array so we can draw it in |
michael@0 | 104 | // a single call. |
michael@0 | 105 | RegionIterator i = new RegionIterator(mMaskedBounds); |
michael@0 | 106 | while (i.next(mSubRect)) { |
michael@0 | 107 | // Compensate for rounding errors at the edge of the tile caused by |
michael@0 | 108 | // the roundOut above |
michael@0 | 109 | mSubRectF.set(Math.max(mBounds.left, (float)mSubRect.left), |
michael@0 | 110 | Math.max(mBounds.top, (float)mSubRect.top), |
michael@0 | 111 | Math.min(mBounds.right, (float)mSubRect.right), |
michael@0 | 112 | Math.min(mBounds.bottom, (float)mSubRect.bottom)); |
michael@0 | 113 | |
michael@0 | 114 | // This is the left/top/right/bottom of the rect, relative to the |
michael@0 | 115 | // bottom-left of the layer, to use for texture coordinates. |
michael@0 | 116 | mCropRect.set(Math.round(mSubRectF.left - mBounds.left), |
michael@0 | 117 | Math.round(mBounds.bottom - mSubRectF.top), |
michael@0 | 118 | Math.round(mSubRectF.right - mBounds.left), |
michael@0 | 119 | Math.round(mBounds.bottom - mSubRectF.bottom)); |
michael@0 | 120 | |
michael@0 | 121 | mObjRectF.set(mSubRectF.left - mViewport.left, |
michael@0 | 122 | mViewport.bottom - mSubRectF.bottom, |
michael@0 | 123 | mSubRectF.right - mViewport.left, |
michael@0 | 124 | mViewport.bottom - mSubRectF.top); |
michael@0 | 125 | |
michael@0 | 126 | fillRectCoordBuffer(mCoords, mObjRectF, mViewport.width(), mViewport.height(), |
michael@0 | 127 | mCropRect, mTextureBounds.width(), mTextureBounds.height()); |
michael@0 | 128 | |
michael@0 | 129 | FloatBuffer coordBuffer = context.coordBuffer; |
michael@0 | 130 | int positionHandle = context.positionHandle; |
michael@0 | 131 | int textureHandle = context.textureHandle; |
michael@0 | 132 | |
michael@0 | 133 | GLES20.glActiveTexture(GLES20.GL_TEXTURE0); |
michael@0 | 134 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTextureID()); |
michael@0 | 135 | |
michael@0 | 136 | // Make sure we are at position zero in the buffer |
michael@0 | 137 | coordBuffer.position(0); |
michael@0 | 138 | coordBuffer.put(mCoords); |
michael@0 | 139 | |
michael@0 | 140 | // Unbind any the current array buffer so we can use client side buffers |
michael@0 | 141 | GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0); |
michael@0 | 142 | |
michael@0 | 143 | // Vertex coordinates are x,y,z starting at position 0 into the buffer. |
michael@0 | 144 | coordBuffer.position(0); |
michael@0 | 145 | GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 20, coordBuffer); |
michael@0 | 146 | |
michael@0 | 147 | // Texture coordinates are texture_x, texture_y starting at position 3 into the buffer. |
michael@0 | 148 | coordBuffer.position(3); |
michael@0 | 149 | GLES20.glVertexAttribPointer(textureHandle, 2, GLES20.GL_FLOAT, false, 20, coordBuffer); |
michael@0 | 150 | GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); |
michael@0 | 151 | } |
michael@0 | 152 | } |
michael@0 | 153 | } |