blob: 797ed103327643f240e02bf0a30e754f7af67fb7 [file] [log] [blame]
Romain Guy06f96e22010-07-30 19:18:16 -07001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "OpenGLRenderer"
18
19#include <utils/Log.h>
20
21#include <SkMatrix.h>
22
Romain Guya1d3c912011-12-13 14:55:06 -080023#include "Caches.h"
Romain Guy06f96e22010-07-30 19:18:16 -070024#include "SkiaShader.h"
25#include "Texture.h"
26#include "Matrix.h"
27
28namespace android {
29namespace uirenderer {
30
31///////////////////////////////////////////////////////////////////////////////
32// Support
33///////////////////////////////////////////////////////////////////////////////
34
Romain Guy06f96e22010-07-30 19:18:16 -070035static const GLint gTileModes[] = {
36 GL_CLAMP_TO_EDGE, // == SkShader::kClamp_TileMode
37 GL_REPEAT, // == SkShader::kRepeat_Mode
38 GL_MIRRORED_REPEAT // == SkShader::kMirror_TileMode
39};
40
Romain Guy42e1e0d2012-07-30 14:47:51 -070041/**
42 * This function does not work for n == 0.
43 */
44static inline bool isPowerOfTwo(unsigned int n) {
45 return !(n & (n - 1));
46}
47
48static inline void bindUniformColor(int slot, uint32_t color) {
Romain Guyd679b572012-08-29 21:49:00 -070049 const float a = ((color >> 24) & 0xff) / 255.0f;
Romain Guy42e1e0d2012-07-30 14:47:51 -070050 glUniform4f(slot,
Romain Guyd679b572012-08-29 21:49:00 -070051 a * ((color >> 16) & 0xff) / 255.0f,
52 a * ((color >> 8) & 0xff) / 255.0f,
53 a * ((color ) & 0xff) / 255.0f,
54 a);
Romain Guy42e1e0d2012-07-30 14:47:51 -070055}
56
Romain Guy06f96e22010-07-30 19:18:16 -070057///////////////////////////////////////////////////////////////////////////////
58// Base shader
59///////////////////////////////////////////////////////////////////////////////
60
Romain Guy24c00212011-01-14 15:31:00 -080061void SkiaShader::copyFrom(const SkiaShader& shader) {
62 mType = shader.mType;
63 mKey = shader.mKey;
64 mTileX = shader.mTileX;
65 mTileY = shader.mTileY;
66 mBlend = shader.mBlend;
67 mUnitMatrix = shader.mUnitMatrix;
68 mShaderMatrix = shader.mShaderMatrix;
69 mGenerationId = shader.mGenerationId;
70}
71
Romain Guy8aa195d2013-06-04 18:00:09 -070072SkiaShader::SkiaShader(): mCaches(NULL) {
73}
74
Romain Guy06f96e22010-07-30 19:18:16 -070075SkiaShader::SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX,
76 SkShader::TileMode tileY, SkMatrix* matrix, bool blend):
Romain Guy8aa195d2013-06-04 18:00:09 -070077 mType(type), mKey(key), mTileX(tileX), mTileY(tileY), mBlend(blend),
78 mCaches(NULL) {
Romain Guy14830942010-10-07 15:07:45 -070079 setMatrix(matrix);
Romain Guy24c00212011-01-14 15:31:00 -080080 mGenerationId = 0;
Romain Guy06f96e22010-07-30 19:18:16 -070081}
82
83SkiaShader::~SkiaShader() {
84}
85
86void SkiaShader::describe(ProgramDescription& description, const Extensions& extensions) {
87}
88
89void SkiaShader::setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
90 GLuint* textureUnit) {
91}
92
Romain Guy01d06572010-11-11 12:06:27 -080093void SkiaShader::bindTexture(Texture* texture, GLenum wrapS, GLenum wrapT) {
Romain Guy8aa195d2013-06-04 18:00:09 -070094 mCaches->bindTexture(texture->id);
Romain Guyd21b6e12011-11-30 20:21:23 -080095 texture->setWrapST(wrapS, wrapT);
Romain Guy06f96e22010-07-30 19:18:16 -070096}
97
Romain Guy14830942010-10-07 15:07:45 -070098void SkiaShader::computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelView) {
99 screenSpace.loadMultiply(mUnitMatrix, mShaderMatrix);
100 screenSpace.multiply(modelView);
101}
102
Romain Guy06f96e22010-07-30 19:18:16 -0700103///////////////////////////////////////////////////////////////////////////////
104// Bitmap shader
105///////////////////////////////////////////////////////////////////////////////
106
107SkiaBitmapShader::SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX,
108 SkShader::TileMode tileY, SkMatrix* matrix, bool blend):
Romain Guy9cccc2b2010-08-07 23:46:15 -0700109 SkiaShader(kBitmap, key, tileX, tileY, matrix, blend), mBitmap(bitmap), mTexture(NULL) {
Romain Guy14830942010-10-07 15:07:45 -0700110 updateLocalMatrix(matrix);
Romain Guy06f96e22010-07-30 19:18:16 -0700111}
112
Romain Guy24c00212011-01-14 15:31:00 -0800113SkiaShader* SkiaBitmapShader::copy() {
114 SkiaBitmapShader* copy = new SkiaBitmapShader();
115 copy->copyFrom(*this);
116 copy->mBitmap = mBitmap;
117 return copy;
118}
119
Romain Guy06f96e22010-07-30 19:18:16 -0700120void SkiaBitmapShader::describe(ProgramDescription& description, const Extensions& extensions) {
Romain Guy8aa195d2013-06-04 18:00:09 -0700121 Texture* texture = mCaches->textureCache.get(mBitmap);
Romain Guy9cccc2b2010-08-07 23:46:15 -0700122 if (!texture) return;
123 mTexture = texture;
Romain Guy06f96e22010-07-30 19:18:16 -0700124
125 const float width = texture->width;
126 const float height = texture->height;
127
128 description.hasBitmap = true;
129 // The driver does not support non-power of two mirrored/repeated
130 // textures, so do it ourselves
Romain Guy61c8c9c2010-08-09 20:48:09 -0700131 if (!extensions.hasNPot() && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) &&
132 (mTileX != SkShader::kClamp_TileMode || mTileY != SkShader::kClamp_TileMode)) {
Romain Guy06f96e22010-07-30 19:18:16 -0700133 description.isBitmapNpot = true;
134 description.bitmapWrapS = gTileModes[mTileX];
135 description.bitmapWrapT = gTileModes[mTileY];
Romain Guy29d89972010-09-22 16:10:57 -0700136 mWrapS = GL_CLAMP_TO_EDGE;
137 mWrapT = GL_CLAMP_TO_EDGE;
138 } else {
139 mWrapS = gTileModes[mTileX];
140 mWrapT = gTileModes[mTileY];
Romain Guy06f96e22010-07-30 19:18:16 -0700141 }
142}
143
144void SkiaBitmapShader::setupProgram(Program* program, const mat4& modelView,
145 const Snapshot& snapshot, GLuint* textureUnit) {
146 GLuint textureSlot = (*textureUnit)++;
Romain Guya1d3c912011-12-13 14:55:06 -0800147 Caches::getInstance().activeTexture(textureSlot);
Romain Guy9cccc2b2010-08-07 23:46:15 -0700148
Romain Guy8164c2d2010-10-25 18:03:28 -0700149 Texture* texture = mTexture;
Romain Guy9cccc2b2010-08-07 23:46:15 -0700150 mTexture = NULL;
151 if (!texture) return;
152 const AutoTexture autoCleanup(texture);
Romain Guy06f96e22010-07-30 19:18:16 -0700153
154 const float width = texture->width;
155 const float height = texture->height;
156
157 mat4 textureTransform;
Romain Guy14830942010-10-07 15:07:45 -0700158 computeScreenSpaceMatrix(textureTransform, modelView);
Romain Guy06f96e22010-07-30 19:18:16 -0700159
160 // Uniforms
Romain Guy01d06572010-11-11 12:06:27 -0800161 bindTexture(texture, mWrapS, mWrapT);
Romain Guyd21b6e12011-11-30 20:21:23 -0800162 texture->setFilter(GL_LINEAR);
Romain Guye3c26852011-07-25 16:36:01 -0700163
Romain Guy06f96e22010-07-30 19:18:16 -0700164 glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
165 glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
166 GL_FALSE, &textureTransform.data[0]);
167 glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height);
168}
169
170///////////////////////////////////////////////////////////////////////////////
171// Linear gradient shader
172///////////////////////////////////////////////////////////////////////////////
173
Romain Guye3095e02010-10-06 16:57:29 -0700174static void toUnitMatrix(const SkPoint pts[2], SkMatrix* matrix) {
175 SkVector vec = pts[1] - pts[0];
176 const float mag = vec.length();
177 const float inv = mag ? 1.0f / mag : 0;
178
179 vec.scale(inv);
180 matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
181 matrix->postTranslate(-pts[0].fX, -pts[0].fY);
182 matrix->postScale(inv, inv);
183}
184
Romain Guy06f96e22010-07-30 19:18:16 -0700185SkiaLinearGradientShader::SkiaLinearGradientShader(float* bounds, uint32_t* colors,
186 float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
187 SkMatrix* matrix, bool blend):
188 SkiaShader(kLinearGradient, key, tileMode, tileMode, matrix, blend),
189 mBounds(bounds), mColors(colors), mPositions(positions), mCount(count) {
Romain Guye3095e02010-10-06 16:57:29 -0700190 SkPoint points[2];
191 points[0].set(bounds[0], bounds[1]);
192 points[1].set(bounds[2], bounds[3]);
193
194 SkMatrix unitMatrix;
195 toUnitMatrix(points, &unitMatrix);
196 mUnitMatrix.load(unitMatrix);
197
198 updateLocalMatrix(matrix);
Romain Guy42e1e0d2012-07-30 14:47:51 -0700199
200 mIsSimple = count == 2 && tileMode == SkShader::kClamp_TileMode;
Romain Guy06f96e22010-07-30 19:18:16 -0700201}
202
203SkiaLinearGradientShader::~SkiaLinearGradientShader() {
Romain Guy25ee0372010-08-06 10:57:58 -0700204 delete[] mBounds;
205 delete[] mColors;
206 delete[] mPositions;
Romain Guy06f96e22010-07-30 19:18:16 -0700207}
208
Romain Guy24c00212011-01-14 15:31:00 -0800209SkiaShader* SkiaLinearGradientShader::copy() {
210 SkiaLinearGradientShader* copy = new SkiaLinearGradientShader();
211 copy->copyFrom(*this);
Romain Guy43ccf462011-01-14 18:51:01 -0800212 copy->mBounds = new float[4];
213 memcpy(copy->mBounds, mBounds, sizeof(float) * 4);
214 copy->mColors = new uint32_t[mCount];
215 memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount);
216 copy->mPositions = new float[mCount];
217 memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
Romain Guy24c00212011-01-14 15:31:00 -0800218 copy->mCount = mCount;
Romain Guy42e1e0d2012-07-30 14:47:51 -0700219 copy->mIsSimple = mIsSimple;
Romain Guy24c00212011-01-14 15:31:00 -0800220 return copy;
221}
222
Romain Guy06f96e22010-07-30 19:18:16 -0700223void SkiaLinearGradientShader::describe(ProgramDescription& description,
224 const Extensions& extensions) {
225 description.hasGradient = true;
Romain Guyee916f12010-09-20 17:53:08 -0700226 description.gradientType = ProgramDescription::kGradientLinear;
Romain Guy42e1e0d2012-07-30 14:47:51 -0700227 description.isSimpleGradient = mIsSimple;
Romain Guy06f96e22010-07-30 19:18:16 -0700228}
229
230void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelView,
231 const Snapshot& snapshot, GLuint* textureUnit) {
Romain Guy42e1e0d2012-07-30 14:47:51 -0700232 if (CC_UNLIKELY(!mIsSimple)) {
233 GLuint textureSlot = (*textureUnit)++;
234 Caches::getInstance().activeTexture(textureSlot);
Romain Guy06f96e22010-07-30 19:18:16 -0700235
Romain Guy8aa195d2013-06-04 18:00:09 -0700236 Texture* texture = mCaches->gradientCache.get(mColors, mPositions, mCount);
Romain Guy42e1e0d2012-07-30 14:47:51 -0700237
238 // Uniforms
239 bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
240 glUniform1i(program->getUniform("gradientSampler"), textureSlot);
241 } else {
242 bindUniformColor(program->getUniform("startColor"), mColors[0]);
243 bindUniformColor(program->getUniform("endColor"), mColors[1]);
244 }
Romain Guy06f96e22010-07-30 19:18:16 -0700245
Romain Guy211efea2012-07-31 21:16:07 -0700246 Caches::getInstance().dither.setupProgram(program, textureUnit);
247
Romain Guye3095e02010-10-06 16:57:29 -0700248 mat4 screenSpace;
249 computeScreenSpaceMatrix(screenSpace, modelView);
Romain Guy06f96e22010-07-30 19:18:16 -0700250 glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
251}
252
253///////////////////////////////////////////////////////////////////////////////
Romain Guyddb80be2010-09-20 19:04:33 -0700254// Circular gradient shader
255///////////////////////////////////////////////////////////////////////////////
256
Romain Guy14830942010-10-07 15:07:45 -0700257static void toCircularUnitMatrix(const float x, const float y, const float radius,
258 SkMatrix* matrix) {
259 const float inv = 1.0f / radius;
260 matrix->setTranslate(-x, -y);
261 matrix->postScale(inv, inv);
262}
263
Romain Guyddb80be2010-09-20 19:04:33 -0700264SkiaCircularGradientShader::SkiaCircularGradientShader(float x, float y, float radius,
265 uint32_t* colors, float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
266 SkMatrix* matrix, bool blend):
267 SkiaSweepGradientShader(kCircularGradient, x, y, colors, positions, count, key,
Romain Guy14830942010-10-07 15:07:45 -0700268 tileMode, matrix, blend) {
269 SkMatrix unitMatrix;
270 toCircularUnitMatrix(x, y, radius, &unitMatrix);
271 mUnitMatrix.load(unitMatrix);
272
273 updateLocalMatrix(matrix);
Romain Guyddb80be2010-09-20 19:04:33 -0700274}
275
Romain Guy24c00212011-01-14 15:31:00 -0800276SkiaShader* SkiaCircularGradientShader::copy() {
277 SkiaCircularGradientShader* copy = new SkiaCircularGradientShader();
278 copy->copyFrom(*this);
Romain Guy43ccf462011-01-14 18:51:01 -0800279 copy->mColors = new uint32_t[mCount];
280 memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount);
281 copy->mPositions = new float[mCount];
282 memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
Romain Guy24c00212011-01-14 15:31:00 -0800283 copy->mCount = mCount;
Romain Guy42e1e0d2012-07-30 14:47:51 -0700284 copy->mIsSimple = mIsSimple;
Romain Guy24c00212011-01-14 15:31:00 -0800285 return copy;
286}
287
Romain Guyddb80be2010-09-20 19:04:33 -0700288void SkiaCircularGradientShader::describe(ProgramDescription& description,
289 const Extensions& extensions) {
290 description.hasGradient = true;
291 description.gradientType = ProgramDescription::kGradientCircular;
Romain Guy42e1e0d2012-07-30 14:47:51 -0700292 description.isSimpleGradient = mIsSimple;
Romain Guyddb80be2010-09-20 19:04:33 -0700293}
294
Romain Guyddb80be2010-09-20 19:04:33 -0700295///////////////////////////////////////////////////////////////////////////////
Romain Guyee916f12010-09-20 17:53:08 -0700296// Sweep gradient shader
297///////////////////////////////////////////////////////////////////////////////
298
Romain Guy14830942010-10-07 15:07:45 -0700299static void toSweepUnitMatrix(const float x, const float y, SkMatrix* matrix) {
300 matrix->setTranslate(-x, -y);
301}
302
Romain Guyee916f12010-09-20 17:53:08 -0700303SkiaSweepGradientShader::SkiaSweepGradientShader(float x, float y, uint32_t* colors,
304 float* positions, int count, SkShader* key, SkMatrix* matrix, bool blend):
305 SkiaShader(kSweepGradient, key, SkShader::kClamp_TileMode,
306 SkShader::kClamp_TileMode, matrix, blend),
Romain Guy14830942010-10-07 15:07:45 -0700307 mColors(colors), mPositions(positions), mCount(count) {
308 SkMatrix unitMatrix;
309 toSweepUnitMatrix(x, y, &unitMatrix);
310 mUnitMatrix.load(unitMatrix);
311
312 updateLocalMatrix(matrix);
Romain Guy42e1e0d2012-07-30 14:47:51 -0700313
314 mIsSimple = count == 2;
Romain Guyee916f12010-09-20 17:53:08 -0700315}
316
Romain Guyddb80be2010-09-20 19:04:33 -0700317SkiaSweepGradientShader::SkiaSweepGradientShader(Type type, float x, float y, uint32_t* colors,
318 float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
319 SkMatrix* matrix, bool blend):
320 SkiaShader(type, key, tileMode, tileMode, matrix, blend),
Romain Guy14830942010-10-07 15:07:45 -0700321 mColors(colors), mPositions(positions), mCount(count) {
Romain Guy42e1e0d2012-07-30 14:47:51 -0700322
323 mIsSimple = count == 2 && tileMode == SkShader::kClamp_TileMode;
Romain Guyddb80be2010-09-20 19:04:33 -0700324}
325
Romain Guyee916f12010-09-20 17:53:08 -0700326SkiaSweepGradientShader::~SkiaSweepGradientShader() {
327 delete[] mColors;
328 delete[] mPositions;
329}
330
Romain Guy24c00212011-01-14 15:31:00 -0800331SkiaShader* SkiaSweepGradientShader::copy() {
332 SkiaSweepGradientShader* copy = new SkiaSweepGradientShader();
333 copy->copyFrom(*this);
Romain Guy43ccf462011-01-14 18:51:01 -0800334 copy->mColors = new uint32_t[mCount];
335 memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount);
336 copy->mPositions = new float[mCount];
337 memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
Romain Guy24c00212011-01-14 15:31:00 -0800338 copy->mCount = mCount;
Romain Guy42e1e0d2012-07-30 14:47:51 -0700339 copy->mIsSimple = mIsSimple;
Romain Guy24c00212011-01-14 15:31:00 -0800340 return copy;
341}
342
Romain Guyee916f12010-09-20 17:53:08 -0700343void SkiaSweepGradientShader::describe(ProgramDescription& description,
344 const Extensions& extensions) {
345 description.hasGradient = true;
346 description.gradientType = ProgramDescription::kGradientSweep;
Romain Guy42e1e0d2012-07-30 14:47:51 -0700347 description.isSimpleGradient = mIsSimple;
Romain Guyee916f12010-09-20 17:53:08 -0700348}
349
350void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelView,
351 const Snapshot& snapshot, GLuint* textureUnit) {
Romain Guy42e1e0d2012-07-30 14:47:51 -0700352 if (CC_UNLIKELY(!mIsSimple)) {
353 GLuint textureSlot = (*textureUnit)++;
354 Caches::getInstance().activeTexture(textureSlot);
Romain Guyee916f12010-09-20 17:53:08 -0700355
Romain Guy8aa195d2013-06-04 18:00:09 -0700356 Texture* texture = mCaches->gradientCache.get(mColors, mPositions, mCount);
Romain Guy42e1e0d2012-07-30 14:47:51 -0700357
358 // Uniforms
359 bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
360 glUniform1i(program->getUniform("gradientSampler"), textureSlot);
361 } else {
362 bindUniformColor(program->getUniform("startColor"), mColors[0]);
363 bindUniformColor(program->getUniform("endColor"), mColors[1]);
364 }
Romain Guyee916f12010-09-20 17:53:08 -0700365
Romain Guy8aa195d2013-06-04 18:00:09 -0700366 mCaches->dither.setupProgram(program, textureUnit);
Romain Guy211efea2012-07-31 21:16:07 -0700367
Romain Guy14830942010-10-07 15:07:45 -0700368 mat4 screenSpace;
369 computeScreenSpaceMatrix(screenSpace, modelView);
Romain Guyee916f12010-09-20 17:53:08 -0700370 glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
371}
372
Romain Guyee916f12010-09-20 17:53:08 -0700373///////////////////////////////////////////////////////////////////////////////
Romain Guy06f96e22010-07-30 19:18:16 -0700374// Compose shader
375///////////////////////////////////////////////////////////////////////////////
376
377SkiaComposeShader::SkiaComposeShader(SkiaShader* first, SkiaShader* second,
378 SkXfermode::Mode mode, SkShader* key):
379 SkiaShader(kCompose, key, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
Romain Guy43ccf462011-01-14 18:51:01 -0800380 NULL, first->blend() || second->blend()),
381 mFirst(first), mSecond(second), mMode(mode), mCleanup(false) {
382}
383
384SkiaComposeShader::~SkiaComposeShader() {
385 if (mCleanup) {
386 delete mFirst;
387 delete mSecond;
388 }
Romain Guy06f96e22010-07-30 19:18:16 -0700389}
390
Romain Guy24c00212011-01-14 15:31:00 -0800391SkiaShader* SkiaComposeShader::copy() {
392 SkiaComposeShader* copy = new SkiaComposeShader();
393 copy->copyFrom(*this);
Romain Guy43ccf462011-01-14 18:51:01 -0800394 copy->mFirst = mFirst->copy();
395 copy->mSecond = mSecond->copy();
Romain Guy24c00212011-01-14 15:31:00 -0800396 copy->mMode = mMode;
Romain Guy43ccf462011-01-14 18:51:01 -0800397 copy->cleanup();
Romain Guy24c00212011-01-14 15:31:00 -0800398 return copy;
399}
400
Romain Guy06f96e22010-07-30 19:18:16 -0700401void SkiaComposeShader::describe(ProgramDescription& description, const Extensions& extensions) {
402 mFirst->describe(description, extensions);
403 mSecond->describe(description, extensions);
404 if (mFirst->type() == kBitmap) {
405 description.isBitmapFirst = true;
406 }
407 description.shadersMode = mMode;
408}
409
410void SkiaComposeShader::setupProgram(Program* program, const mat4& modelView,
411 const Snapshot& snapshot, GLuint* textureUnit) {
Romain Guyd42899222013-03-18 19:30:48 -0700412 // Apply this compose shader's local transform and pass it down to
413 // the child shaders. They will in turn apply their local transform
414 // to this matrix.
415 mat4 transform;
416 computeScreenSpaceMatrix(transform, modelView);
417
418 mFirst->setupProgram(program, transform, snapshot, textureUnit);
419 mSecond->setupProgram(program, transform, snapshot, textureUnit);
Romain Guy06f96e22010-07-30 19:18:16 -0700420}
421
422}; // namespace uirenderer
423}; // namespace android