• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
23 #include "Caches.h"
24 #include "Layer.h"
25 #include "Matrix.h"
26 #include "SkiaShader.h"
27 #include "Texture.h"
28 
29 namespace android {
30 namespace uirenderer {
31 
32 ///////////////////////////////////////////////////////////////////////////////
33 // Support
34 ///////////////////////////////////////////////////////////////////////////////
35 
36 static const GLint gTileModes[] = {
37         GL_CLAMP_TO_EDGE,   // == SkShader::kClamp_TileMode
38         GL_REPEAT,          // == SkShader::kRepeat_Mode
39         GL_MIRRORED_REPEAT  // == SkShader::kMirror_TileMode
40 };
41 
42 /**
43  * This function does not work for n == 0.
44  */
isPowerOfTwo(unsigned int n)45 static inline bool isPowerOfTwo(unsigned int n) {
46     return !(n & (n - 1));
47 }
48 
bindUniformColor(int slot,uint32_t color)49 static inline void bindUniformColor(int slot, uint32_t color) {
50     const float a = ((color >> 24) & 0xff) / 255.0f;
51     glUniform4f(slot,
52             a * ((color >> 16) & 0xff) / 255.0f,
53             a * ((color >>  8) & 0xff) / 255.0f,
54             a * ((color      ) & 0xff) / 255.0f,
55             a);
56 }
57 
bindTexture(Caches * caches,Texture * texture,GLenum wrapS,GLenum wrapT)58 static inline void bindTexture(Caches* caches, Texture* texture, GLenum wrapS, GLenum wrapT) {
59     caches->bindTexture(texture->id);
60     texture->setWrapST(wrapS, wrapT);
61 }
62 
63 /**
64  * Compute the matrix to transform to screen space.
65  * @param screenSpace Output param for the computed matrix.
66  * @param unitMatrix The unit matrix for gradient shaders, as returned by SkShader::asAGradient,
67  *      or identity.
68  * @param localMatrix Local matrix, as returned by SkShader::getLocalMatrix().
69  * @param modelViewMatrix Model view matrix, as supplied by the OpenGLRenderer.
70  */
computeScreenSpaceMatrix(mat4 & screenSpace,const SkMatrix & unitMatrix,const SkMatrix & localMatrix,const mat4 & modelViewMatrix)71 static void computeScreenSpaceMatrix(mat4& screenSpace, const SkMatrix& unitMatrix,
72         const SkMatrix& localMatrix, const mat4& modelViewMatrix) {
73     mat4 shaderMatrix;
74     // uses implicit construction
75     shaderMatrix.loadInverse(localMatrix);
76     // again, uses implicit construction
77     screenSpace.loadMultiply(unitMatrix, shaderMatrix);
78     screenSpace.multiply(modelViewMatrix);
79 }
80 
81 // Returns true if one is a bitmap and the other is a gradient
bitmapAndGradient(SkiaShaderType type1,SkiaShaderType type2)82 static bool bitmapAndGradient(SkiaShaderType type1, SkiaShaderType type2) {
83     return (type1 == kBitmap_SkiaShaderType && type2 == kGradient_SkiaShaderType)
84             || (type2 == kBitmap_SkiaShaderType && type1 == kGradient_SkiaShaderType);
85 }
86 
getType(const SkShader & shader)87 SkiaShaderType SkiaShader::getType(const SkShader& shader) {
88     // First check for a gradient shader.
89     switch (shader.asAGradient(NULL)) {
90         case SkShader::kNone_GradientType:
91             // Not a gradient shader. Fall through to check for other types.
92             break;
93         case SkShader::kLinear_GradientType:
94         case SkShader::kRadial_GradientType:
95         case SkShader::kSweep_GradientType:
96             return kGradient_SkiaShaderType;
97         default:
98             // This is a Skia gradient that has no SkiaShader equivalent. Return None to skip.
99             return kNone_SkiaShaderType;
100     }
101 
102     // The shader is not a gradient. Check for a bitmap shader.
103     if (shader.asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType) {
104         return kBitmap_SkiaShaderType;
105     }
106 
107     // Check for a ComposeShader.
108     SkShader::ComposeRec rec;
109     if (shader.asACompose(&rec)) {
110         const SkiaShaderType shaderAType = getType(*rec.fShaderA);
111         const SkiaShaderType shaderBType = getType(*rec.fShaderB);
112 
113         // Compose is only supported if one is a bitmap and the other is a
114         // gradient. Otherwise, return None to skip.
115         if (!bitmapAndGradient(shaderAType, shaderBType)) {
116             return kNone_SkiaShaderType;
117         }
118         return kCompose_SkiaShaderType;
119     }
120 
121     if (shader.asACustomShader(NULL)) {
122         return kLayer_SkiaShaderType;
123     }
124 
125     return kNone_SkiaShaderType;
126 }
127 
128 typedef void (*describeProc)(Caches* caches, ProgramDescription& description,
129         const Extensions& extensions, const SkShader& shader);
130 
131 describeProc gDescribeProc[] = {
132     InvalidSkiaShader::describe,
133     SkiaBitmapShader::describe,
134     SkiaGradientShader::describe,
135     SkiaComposeShader::describe,
136     SkiaLayerShader::describe,
137 };
138 
139 typedef void (*setupProgramProc)(Caches* caches, const mat4& modelViewMatrix,
140         GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
141 
142 setupProgramProc gSetupProgramProc[] = {
143     InvalidSkiaShader::setupProgram,
144     SkiaBitmapShader::setupProgram,
145     SkiaGradientShader::setupProgram,
146     SkiaComposeShader::setupProgram,
147     SkiaLayerShader::setupProgram,
148 };
149 
describe(Caches * caches,ProgramDescription & description,const Extensions & extensions,const SkShader & shader)150 void SkiaShader::describe(Caches* caches, ProgramDescription& description,
151         const Extensions& extensions, const SkShader& shader) {
152     gDescribeProc[getType(shader)](caches, description, extensions, shader);
153 }
154 
setupProgram(Caches * caches,const mat4 & modelViewMatrix,GLuint * textureUnit,const Extensions & extensions,const SkShader & shader)155 void SkiaShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
156         GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) {
157 
158     gSetupProgramProc[getType(shader)](caches, modelViewMatrix, textureUnit, extensions, shader);
159 }
160 
161 ///////////////////////////////////////////////////////////////////////////////
162 // Layer shader
163 ///////////////////////////////////////////////////////////////////////////////
164 
describe(Caches *,ProgramDescription & description,const Extensions &,const SkShader & shader)165 void SkiaLayerShader::describe(Caches*, ProgramDescription& description,
166         const Extensions&, const SkShader& shader) {
167     description.hasBitmap = true;
168 }
169 
setupProgram(Caches * caches,const mat4 & modelViewMatrix,GLuint * textureUnit,const Extensions &,const SkShader & shader)170 void SkiaLayerShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
171         GLuint* textureUnit, const Extensions&, const SkShader& shader) {
172     Layer* layer;
173     if (!shader.asACustomShader(reinterpret_cast<void**>(&layer))) {
174         LOG_ALWAYS_FATAL("SkiaLayerShader::setupProgram called on the wrong type of shader!");
175     }
176 
177     GLuint textureSlot = (*textureUnit)++;
178     caches->activeTexture(textureSlot);
179 
180     const float width = layer->getWidth();
181     const float height = layer->getHeight();
182 
183     mat4 textureTransform;
184     computeScreenSpaceMatrix(textureTransform, SkMatrix::I(), shader.getLocalMatrix(),
185             modelViewMatrix);
186 
187 
188     // Uniforms
189     layer->bindTexture();
190     layer->setWrap(GL_CLAMP_TO_EDGE);
191     layer->setFilter(GL_LINEAR);
192 
193     Program* program = caches->currentProgram;
194     glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
195     glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
196             GL_FALSE, &textureTransform.data[0]);
197     glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height);
198 }
199 
200 ///////////////////////////////////////////////////////////////////////////////
201 // Bitmap shader
202 ///////////////////////////////////////////////////////////////////////////////
203 
204 struct BitmapShaderInfo {
205     float width;
206     float height;
207     GLenum wrapS;
208     GLenum wrapT;
209     Texture* texture;
210 };
211 
bitmapShaderHelper(Caches * caches,ProgramDescription * description,BitmapShaderInfo * shaderInfo,const Extensions & extensions,const SkBitmap & bitmap,SkShader::TileMode tileModes[2])212 static bool bitmapShaderHelper(Caches* caches, ProgramDescription* description,
213         BitmapShaderInfo* shaderInfo,
214         const Extensions& extensions,
215         const SkBitmap& bitmap, SkShader::TileMode tileModes[2]) {
216     Texture* texture = caches->textureCache.get(&bitmap);
217     if (!texture) return false;
218 
219     const float width = texture->width;
220     const float height = texture->height;
221     GLenum wrapS, wrapT;
222 
223     if (description) {
224         description->hasBitmap = true;
225     }
226     // The driver does not support non-power of two mirrored/repeated
227     // textures, so do it ourselves
228     if (!extensions.hasNPot() && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) &&
229             (tileModes[0] != SkShader::kClamp_TileMode ||
230              tileModes[1] != SkShader::kClamp_TileMode)) {
231         if (description) {
232             description->isBitmapNpot = true;
233             description->bitmapWrapS = gTileModes[tileModes[0]];
234             description->bitmapWrapT = gTileModes[tileModes[1]];
235         }
236         wrapS = GL_CLAMP_TO_EDGE;
237         wrapT = GL_CLAMP_TO_EDGE;
238     } else {
239         wrapS = gTileModes[tileModes[0]];
240         wrapT = gTileModes[tileModes[1]];
241     }
242 
243     if (shaderInfo) {
244         shaderInfo->width = width;
245         shaderInfo->height = height;
246         shaderInfo->wrapS = wrapS;
247         shaderInfo->wrapT = wrapT;
248         shaderInfo->texture = texture;
249     }
250     return true;
251 }
252 
describe(Caches * caches,ProgramDescription & description,const Extensions & extensions,const SkShader & shader)253 void SkiaBitmapShader::describe(Caches* caches, ProgramDescription& description,
254         const Extensions& extensions, const SkShader& shader) {
255     SkBitmap bitmap;
256     SkShader::TileMode xy[2];
257     if (shader.asABitmap(&bitmap, NULL, xy) != SkShader::kDefault_BitmapType) {
258         LOG_ALWAYS_FATAL("SkiaBitmapShader::describe called with a different kind of shader!");
259     }
260     bitmapShaderHelper(caches, &description, NULL, extensions, bitmap, xy);
261 }
262 
setupProgram(Caches * caches,const mat4 & modelViewMatrix,GLuint * textureUnit,const Extensions & extensions,const SkShader & shader)263 void SkiaBitmapShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
264         GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) {
265     SkBitmap bitmap;
266     SkShader::TileMode xy[2];
267     if (shader.asABitmap(&bitmap, NULL, xy) != SkShader::kDefault_BitmapType) {
268         LOG_ALWAYS_FATAL("SkiaBitmapShader::setupProgram called with a different kind of shader!");
269     }
270 
271     GLuint textureSlot = (*textureUnit)++;
272     Caches::getInstance().activeTexture(textureSlot);
273 
274     BitmapShaderInfo shaderInfo;
275     if (!bitmapShaderHelper(caches, NULL, &shaderInfo, extensions, bitmap, xy)) {
276         return;
277     }
278 
279     Program* program = caches->currentProgram;
280     Texture* texture = shaderInfo.texture;
281 
282     const AutoTexture autoCleanup(texture);
283 
284     mat4 textureTransform;
285     computeScreenSpaceMatrix(textureTransform, SkMatrix::I(), shader.getLocalMatrix(),
286             modelViewMatrix);
287 
288     // Uniforms
289     bindTexture(caches, texture, shaderInfo.wrapS, shaderInfo.wrapT);
290     texture->setFilter(GL_LINEAR);
291 
292     glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
293     glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
294             GL_FALSE, &textureTransform.data[0]);
295     glUniform2f(program->getUniform("textureDimension"), 1.0f / shaderInfo.width,
296             1.0f / shaderInfo.height);
297 }
298 
299 ///////////////////////////////////////////////////////////////////////////////
300 // Linear gradient shader
301 ///////////////////////////////////////////////////////////////////////////////
302 
toUnitMatrix(const SkPoint pts[2],SkMatrix * matrix)303 static void toUnitMatrix(const SkPoint pts[2], SkMatrix* matrix) {
304     SkVector vec = pts[1] - pts[0];
305     const float mag = vec.length();
306     const float inv = mag ? 1.0f / mag : 0;
307 
308     vec.scale(inv);
309     matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
310     matrix->postTranslate(-pts[0].fX, -pts[0].fY);
311     matrix->postScale(inv, inv);
312 }
313 
314 ///////////////////////////////////////////////////////////////////////////////
315 // Circular gradient shader
316 ///////////////////////////////////////////////////////////////////////////////
317 
toCircularUnitMatrix(const float x,const float y,const float radius,SkMatrix * matrix)318 static void toCircularUnitMatrix(const float x, const float y, const float radius,
319         SkMatrix* matrix) {
320     const float inv = 1.0f / radius;
321     matrix->setTranslate(-x, -y);
322     matrix->postScale(inv, inv);
323 }
324 
325 ///////////////////////////////////////////////////////////////////////////////
326 // Sweep gradient shader
327 ///////////////////////////////////////////////////////////////////////////////
328 
toSweepUnitMatrix(const float x,const float y,SkMatrix * matrix)329 static void toSweepUnitMatrix(const float x, const float y, SkMatrix* matrix) {
330     matrix->setTranslate(-x, -y);
331 }
332 
333 ///////////////////////////////////////////////////////////////////////////////
334 // Common gradient code
335 ///////////////////////////////////////////////////////////////////////////////
336 
isSimpleGradient(const SkShader::GradientInfo & gradInfo)337 static bool isSimpleGradient(const SkShader::GradientInfo& gradInfo) {
338     return gradInfo.fColorCount == 2 && gradInfo.fTileMode == SkShader::kClamp_TileMode;
339 }
340 
describe(Caches *,ProgramDescription & description,const Extensions & extensions,const SkShader & shader)341 void SkiaGradientShader::describe(Caches*, ProgramDescription& description,
342         const Extensions& extensions, const SkShader& shader) {
343     SkShader::GradientInfo gradInfo;
344     gradInfo.fColorCount = 0;
345     gradInfo.fColors = NULL;
346     gradInfo.fColorOffsets = NULL;
347 
348     switch (shader.asAGradient(&gradInfo)) {
349         case SkShader::kLinear_GradientType:
350             description.gradientType = ProgramDescription::kGradientLinear;
351             break;
352         case SkShader::kRadial_GradientType:
353             description.gradientType = ProgramDescription::kGradientCircular;
354             break;
355         case SkShader::kSweep_GradientType:
356             description.gradientType = ProgramDescription::kGradientSweep;
357             break;
358         default:
359             // Do nothing. This shader is unsupported.
360             return;
361     }
362     description.hasGradient = true;
363     description.isSimpleGradient = isSimpleGradient(gradInfo);
364 }
365 
setupProgram(Caches * caches,const mat4 & modelViewMatrix,GLuint * textureUnit,const Extensions &,const SkShader & shader)366 void SkiaGradientShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
367         GLuint* textureUnit, const Extensions&, const SkShader& shader) {
368     // SkShader::GradientInfo.fColorCount is an in/out parameter. As input, it tells asAGradient
369     // how much space has been allocated for fColors and fColorOffsets.  10 was chosen
370     // arbitrarily, but should be >= 2.
371     // As output, it tells the number of actual colors/offsets in the gradient.
372     const int COLOR_COUNT = 10;
373     SkAutoSTMalloc<COLOR_COUNT, SkColor> colorStorage(COLOR_COUNT);
374     SkAutoSTMalloc<COLOR_COUNT, SkScalar> positionStorage(COLOR_COUNT);
375 
376     SkShader::GradientInfo gradInfo;
377     gradInfo.fColorCount = COLOR_COUNT;
378     gradInfo.fColors = colorStorage.get();
379     gradInfo.fColorOffsets = positionStorage.get();
380 
381     SkShader::GradientType gradType = shader.asAGradient(&gradInfo);
382 
383     Program* program = caches->currentProgram;
384     if (CC_UNLIKELY(!isSimpleGradient(gradInfo))) {
385         if (gradInfo.fColorCount > COLOR_COUNT) {
386             // There was not enough room in our arrays for all the colors and offsets. Try again,
387             // now that we know the true number of colors.
388             gradInfo.fColors = colorStorage.reset(gradInfo.fColorCount);
389             gradInfo.fColorOffsets = positionStorage.reset(gradInfo.fColorCount);
390 
391             shader.asAGradient(&gradInfo);
392         }
393         GLuint textureSlot = (*textureUnit)++;
394         caches->activeTexture(textureSlot);
395 
396 #ifndef SK_SCALAR_IS_FLOAT
397     #error Need to convert gradInfo.fColorOffsets to float!
398 #endif
399         Texture* texture = caches->gradientCache.get(gradInfo.fColors, gradInfo.fColorOffsets,
400                 gradInfo.fColorCount);
401 
402         // Uniforms
403         bindTexture(caches, texture, gTileModes[gradInfo.fTileMode], gTileModes[gradInfo.fTileMode]);
404         glUniform1i(program->getUniform("gradientSampler"), textureSlot);
405     } else {
406         bindUniformColor(program->getUniform("startColor"), gradInfo.fColors[0]);
407         bindUniformColor(program->getUniform("endColor"), gradInfo.fColors[1]);
408     }
409 
410     caches->dither.setupProgram(program, textureUnit);
411 
412     SkMatrix unitMatrix;
413     switch (gradType) {
414         case SkShader::kLinear_GradientType:
415             toUnitMatrix(gradInfo.fPoint, &unitMatrix);
416             break;
417         case SkShader::kRadial_GradientType:
418             toCircularUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY,
419                     gradInfo.fRadius[0], &unitMatrix);
420             break;
421         case SkShader::kSweep_GradientType:
422             toSweepUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, &unitMatrix);
423             break;
424         default:
425             LOG_ALWAYS_FATAL("Invalid SkShader gradient type %d", gradType);
426     }
427 
428     mat4 screenSpace;
429     computeScreenSpaceMatrix(screenSpace, unitMatrix, shader.getLocalMatrix(), modelViewMatrix);
430     glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
431 }
432 
433 ///////////////////////////////////////////////////////////////////////////////
434 // Compose shader
435 ///////////////////////////////////////////////////////////////////////////////
436 
describe(Caches * caches,ProgramDescription & description,const Extensions & extensions,const SkShader & shader)437 void SkiaComposeShader::describe(Caches* caches, ProgramDescription& description,
438         const Extensions& extensions, const SkShader& shader) {
439     SkShader::ComposeRec rec;
440     if (!shader.asACompose(&rec)) {
441         LOG_ALWAYS_FATAL("SkiaComposeShader::describe called on the wrong shader type!");
442     }
443     SkiaShader::describe(caches, description, extensions, *rec.fShaderA);
444     SkiaShader::describe(caches, description, extensions, *rec.fShaderB);
445     if (SkiaShader::getType(*rec.fShaderA) == kBitmap_SkiaShaderType) {
446         description.isBitmapFirst = true;
447     }
448     if (!SkXfermode::AsMode(rec.fMode, &description.shadersMode)) {
449         // TODO: Support other modes.
450         description.shadersMode = SkXfermode::kSrcOver_Mode;
451     }
452 }
453 
setupProgram(Caches * caches,const mat4 & modelViewMatrix,GLuint * textureUnit,const Extensions & extensions,const SkShader & shader)454 void SkiaComposeShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
455         GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) {
456     SkShader::ComposeRec rec;
457     if (!shader.asACompose(&rec)) {
458         LOG_ALWAYS_FATAL("SkiaComposeShader::setupProgram called on the wrong shader type!");
459     }
460 
461     // Apply this compose shader's local transform and pass it down to
462     // the child shaders. They will in turn apply their local transform
463     // to this matrix.
464     mat4 transform;
465     computeScreenSpaceMatrix(transform, SkMatrix::I(), shader.getLocalMatrix(),
466             modelViewMatrix);
467 
468     SkiaShader::setupProgram(caches, transform, textureUnit, extensions, *rec.fShaderA);
469     SkiaShader::setupProgram(caches, transform, textureUnit, extensions, *rec.fShaderB);
470 }
471 
472 }; // namespace uirenderer
473 }; // namespace android
474