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