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