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