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