|
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 |
|
39 public SingleTileLayer(CairoImage image) { |
|
40 this(false, image); |
|
41 } |
|
42 |
|
43 public SingleTileLayer(boolean repeat, CairoImage image) { |
|
44 this(image, repeat ? TileLayer.PaintMode.REPEAT : TileLayer.PaintMode.NORMAL); |
|
45 } |
|
46 |
|
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 */ |
|
65 public void setMask(Rect aMaskRect) { |
|
66 mMask = aMaskRect; |
|
67 } |
|
68 |
|
69 @Override |
|
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 } |