SkiaShader.cpp revision 011550e1f2f20e66c429c2d854dcaf18c1f6c30c
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#include "SkiaShader.h" 18 19#include "Caches.h" 20#include "Extensions.h" 21#include "Layer.h" 22#include "Matrix.h" 23#include "Texture.h" 24 25#include <SkMatrix.h> 26#include <utils/Log.h> 27 28namespace android { 29namespace uirenderer { 30 31/////////////////////////////////////////////////////////////////////////////// 32// Support 33/////////////////////////////////////////////////////////////////////////////// 34 35static const GLenum gTileModes[] = { 36 GL_CLAMP_TO_EDGE, // == SkShader::kClamp_TileMode 37 GL_REPEAT, // == SkShader::kRepeat_Mode 38 GL_MIRRORED_REPEAT // == SkShader::kMirror_TileMode 39}; 40 41/** 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, FloatColor color) { 49 glUniform4fv(slot, 1, reinterpret_cast<const float*>(&color)); 50} 51 52static inline void bindTexture(Caches* caches, Texture* texture, GLenum wrapS, GLenum wrapT) { 53 caches->textureState().bindTexture(texture->id); 54 texture->setWrapST(wrapS, wrapT); 55} 56 57/** 58 * Compute the matrix to transform to screen space. 59 * @param screenSpace Output param for the computed matrix. 60 * @param unitMatrix The unit matrix for gradient shaders, as returned by SkShader::asAGradient, 61 * or identity. 62 * @param localMatrix Local matrix, as returned by SkShader::getLocalMatrix(). 63 * @param modelViewMatrix Model view matrix, as supplied by the OpenGLRenderer. 64 */ 65static void computeScreenSpaceMatrix(mat4& screenSpace, const SkMatrix& unitMatrix, 66 const SkMatrix& localMatrix, const mat4& modelViewMatrix) { 67 mat4 shaderMatrix; 68 // uses implicit construction 69 shaderMatrix.loadInverse(localMatrix); 70 // again, uses implicit construction 71 screenSpace.loadMultiply(unitMatrix, shaderMatrix); 72 screenSpace.multiply(modelViewMatrix); 73} 74 75/////////////////////////////////////////////////////////////////////////////// 76// gradient shader matrix helpers 77/////////////////////////////////////////////////////////////////////////////// 78 79static void toLinearUnitMatrix(const SkPoint pts[2], SkMatrix* matrix) { 80 SkVector vec = pts[1] - pts[0]; 81 const float mag = vec.length(); 82 const float inv = mag ? 1.0f / mag : 0; 83 84 vec.scale(inv); 85 matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY); 86 matrix->postTranslate(-pts[0].fX, -pts[0].fY); 87 matrix->postScale(inv, inv); 88} 89 90static void toCircularUnitMatrix(const float x, const float y, const float radius, 91 SkMatrix* matrix) { 92 const float inv = 1.0f / radius; 93 matrix->setTranslate(-x, -y); 94 matrix->postScale(inv, inv); 95} 96 97static void toSweepUnitMatrix(const float x, const float y, SkMatrix* matrix) { 98 matrix->setTranslate(-x, -y); 99} 100 101/////////////////////////////////////////////////////////////////////////////// 102// Common gradient code 103/////////////////////////////////////////////////////////////////////////////// 104 105static bool isSimpleGradient(const SkShader::GradientInfo& gradInfo) { 106 return gradInfo.fColorCount == 2 && gradInfo.fTileMode == SkShader::kClamp_TileMode; 107} 108 109/////////////////////////////////////////////////////////////////////////////// 110// Store / apply 111/////////////////////////////////////////////////////////////////////////////// 112 113bool tryStoreGradient(Caches& caches, const SkShader& shader, const Matrix4 modelViewMatrix, 114 GLuint* textureUnit, ProgramDescription* description, 115 SkiaShaderData::GradientShaderData* outData) { 116 SkShader::GradientInfo gradInfo; 117 gradInfo.fColorCount = 0; 118 gradInfo.fColors = nullptr; 119 gradInfo.fColorOffsets = nullptr; 120 121 SkMatrix unitMatrix; 122 switch (shader.asAGradient(&gradInfo)) { 123 case SkShader::kLinear_GradientType: 124 description->gradientType = ProgramDescription::kGradientLinear; 125 126 toLinearUnitMatrix(gradInfo.fPoint, &unitMatrix); 127 break; 128 case SkShader::kRadial_GradientType: 129 description->gradientType = ProgramDescription::kGradientCircular; 130 131 toCircularUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, 132 gradInfo.fRadius[0], &unitMatrix); 133 break; 134 case SkShader::kSweep_GradientType: 135 description->gradientType = ProgramDescription::kGradientSweep; 136 137 toSweepUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, &unitMatrix); 138 break; 139 default: 140 // Do nothing. This shader is unsupported. 141 return false; 142 } 143 description->hasGradient = true; 144 description->isSimpleGradient = isSimpleGradient(gradInfo); 145 146 computeScreenSpaceMatrix(outData->screenSpace, unitMatrix, 147 shader.getLocalMatrix(), modelViewMatrix); 148 149 // re-query shader to get full color / offset data 150 std::unique_ptr<SkColor[]> colorStorage(new SkColor[gradInfo.fColorCount]); 151 std::unique_ptr<SkScalar[]> colorOffsets(new SkScalar[gradInfo.fColorCount]); 152 gradInfo.fColors = &colorStorage[0]; 153 gradInfo.fColorOffsets = &colorOffsets[0]; 154 shader.asAGradient(&gradInfo); 155 156 if (CC_UNLIKELY(!isSimpleGradient(gradInfo))) { 157 outData->gradientSampler = (*textureUnit)++; 158 159#ifndef SK_SCALAR_IS_FLOAT 160 #error Need to convert gradInfo.fColorOffsets to float! 161#endif 162 outData->gradientTexture = caches.gradientCache.get( 163 gradInfo.fColors, gradInfo.fColorOffsets, gradInfo.fColorCount); 164 outData->wrapST = gTileModes[gradInfo.fTileMode]; 165 } else { 166 outData->gradientSampler = 0; 167 outData->gradientTexture = nullptr; 168 169 outData->startColor.set(gradInfo.fColors[0]); 170 outData->endColor.set(gradInfo.fColors[1]); 171 } 172 173 outData->ditherSampler = (*textureUnit)++; 174 return true; 175} 176 177void applyGradient(Caches& caches, const SkiaShaderData::GradientShaderData& data) { 178 if (CC_UNLIKELY(data.gradientTexture)) { 179 caches.textureState().activateTexture(data.gradientSampler); 180 bindTexture(&caches, data.gradientTexture, data.wrapST, data.wrapST); 181 glUniform1i(caches.program().getUniform("gradientSampler"), data.gradientSampler); 182 } else { 183 bindUniformColor(caches.program().getUniform("startColor"), data.startColor); 184 bindUniformColor(caches.program().getUniform("endColor"), data.endColor); 185 } 186 187 // TODO: remove sampler slot incrementing from dither.setupProgram, 188 // since this assignment of slots is done at store, not apply time 189 GLuint ditherSampler = data.ditherSampler; 190 caches.dither.setupProgram(caches.program(), &ditherSampler); 191 glUniformMatrix4fv(caches.program().getUniform("screenSpace"), 1, 192 GL_FALSE, &data.screenSpace.data[0]); 193} 194 195bool tryStoreBitmap(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix, 196 GLuint* textureUnit, ProgramDescription* description, 197 SkiaShaderData::BitmapShaderData* outData) { 198 SkBitmap bitmap; 199 SkShader::TileMode xy[2]; 200 if (shader.asABitmap(&bitmap, nullptr, xy) != SkShader::kDefault_BitmapType) { 201 return false; 202 } 203 204 /* 205 * Bypass the AssetAtlas, since those textures: 206 * 1) require UV mapping, which isn't implemented in matrix computation below 207 * 2) can't handle REPEAT simply 208 * 3) are safe to upload here (outside of sync stage), since they're static 209 */ 210 outData->bitmapTexture = caches.textureCache.getAndBypassAtlas(&bitmap); 211 if (!outData->bitmapTexture) return false; 212 213 outData->bitmapSampler = (*textureUnit)++; 214 215 const float width = outData->bitmapTexture->width; 216 const float height = outData->bitmapTexture->height; 217 218 description->hasBitmap = true; 219 if (!caches.extensions().hasNPot() 220 && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) 221 && (xy[0] != SkShader::kClamp_TileMode || xy[1] != SkShader::kClamp_TileMode)) { 222 description->isBitmapNpot = true; 223 description->bitmapWrapS = gTileModes[xy[0]]; 224 description->bitmapWrapT = gTileModes[xy[1]]; 225 226 outData->wrapS = GL_CLAMP_TO_EDGE; 227 outData->wrapT = GL_CLAMP_TO_EDGE; 228 } else { 229 outData->wrapS = gTileModes[xy[0]]; 230 outData->wrapT = gTileModes[xy[1]]; 231 } 232 233 computeScreenSpaceMatrix(outData->textureTransform, SkMatrix::I(), shader.getLocalMatrix(), 234 modelViewMatrix); 235 outData->textureDimension[0] = 1.0f / width; 236 outData->textureDimension[1] = 1.0f / height; 237 238 return true; 239} 240 241void applyBitmap(Caches& caches, const SkiaShaderData::BitmapShaderData& data) { 242 caches.textureState().activateTexture(data.bitmapSampler); 243 bindTexture(&caches, data.bitmapTexture, data.wrapS, data.wrapT); 244 data.bitmapTexture->setFilter(GL_LINEAR); 245 246 glUniform1i(caches.program().getUniform("bitmapSampler"), data.bitmapSampler); 247 glUniformMatrix4fv(caches.program().getUniform("textureTransform"), 1, GL_FALSE, 248 &data.textureTransform.data[0]); 249 glUniform2fv(caches.program().getUniform("textureDimension"), 1, &data.textureDimension[0]); 250} 251 252SkiaShaderType getComposeSubType(const SkShader& shader) { 253 // First check for a gradient shader. 254 switch (shader.asAGradient(nullptr)) { 255 case SkShader::kNone_GradientType: 256 // Not a gradient shader. Fall through to check for other types. 257 break; 258 case SkShader::kLinear_GradientType: 259 case SkShader::kRadial_GradientType: 260 case SkShader::kSweep_GradientType: 261 return kGradient_SkiaShaderType; 262 default: 263 // This is a Skia gradient that has no SkiaShader equivalent. Return None to skip. 264 return kNone_SkiaShaderType; 265 } 266 267 // The shader is not a gradient. Check for a bitmap shader. 268 if (shader.asABitmap(nullptr, nullptr, nullptr) == SkShader::kDefault_BitmapType) { 269 return kBitmap_SkiaShaderType; 270 } 271 return kNone_SkiaShaderType; 272} 273 274void storeCompose(Caches& caches, const SkShader& bitmapShader, const SkShader& gradientShader, 275 const Matrix4& modelViewMatrix, GLuint* textureUnit, 276 ProgramDescription* description, SkiaShaderData* outData) { 277 LOG_ALWAYS_FATAL_IF(!tryStoreBitmap(caches, bitmapShader, modelViewMatrix, 278 textureUnit, description, &outData->bitmapData), 279 "failed storing bitmap shader data"); 280 LOG_ALWAYS_FATAL_IF(!tryStoreGradient(caches, gradientShader, modelViewMatrix, 281 textureUnit, description, &outData->gradientData), 282 "failing storing gradient shader data"); 283} 284 285bool tryStoreCompose(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix, 286 GLuint* textureUnit, ProgramDescription* description, 287 SkiaShaderData* outData) { 288 289 SkShader::ComposeRec rec; 290 if (!shader.asACompose(&rec)) return false; 291 292 const SkiaShaderType shaderAType = getComposeSubType(*rec.fShaderA); 293 const SkiaShaderType shaderBType = getComposeSubType(*rec.fShaderB); 294 295 // check that type enum values are the 2 flags that compose the kCompose value 296 if ((shaderAType & shaderBType) != 0) return false; 297 if ((shaderAType | shaderBType) != kCompose_SkiaShaderType) return false; 298 299 mat4 transform; 300 computeScreenSpaceMatrix(transform, SkMatrix::I(), shader.getLocalMatrix(), modelViewMatrix); 301 if (shaderAType == kBitmap_SkiaShaderType) { 302 description->isBitmapFirst = true; 303 storeCompose(caches, *rec.fShaderA, *rec.fShaderB, 304 transform, textureUnit, description, outData); 305 } else { 306 description->isBitmapFirst = false; 307 storeCompose(caches, *rec.fShaderB, *rec.fShaderA, 308 transform, textureUnit, description, outData); 309 } 310 if (!SkXfermode::AsMode(rec.fMode, &description->shadersMode)) { 311 // TODO: Support other modes. 312 description->shadersMode = SkXfermode::kSrcOver_Mode; 313 } 314 return true; 315} 316 317bool tryStoreLayer(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix, 318 GLuint* textureUnit, ProgramDescription* description, 319 SkiaShaderData::LayerShaderData* outData) { 320 Layer* layer; 321 if (!shader.asACustomShader(reinterpret_cast<void**>(&layer))) { 322 return false; 323 } 324 325 description->hasBitmap = true; 326 outData->layer = layer; 327 outData->bitmapSampler = (*textureUnit)++; 328 329 const float width = layer->getWidth(); 330 const float height = layer->getHeight(); 331 332 computeScreenSpaceMatrix(outData->textureTransform, SkMatrix::I(), shader.getLocalMatrix(), 333 modelViewMatrix); 334 335 outData->textureDimension[0] = 1.0f / width; 336 outData->textureDimension[1] = 1.0f / height; 337 return true; 338} 339 340void applyLayer(Caches& caches, const SkiaShaderData::LayerShaderData& data) { 341 caches.textureState().activateTexture(data.bitmapSampler); 342 343 data.layer->bindTexture(); 344 data.layer->setWrap(GL_CLAMP_TO_EDGE); 345 data.layer->setFilter(GL_LINEAR); 346 347 glUniform1i(caches.program().getUniform("bitmapSampler"), data.bitmapSampler); 348 glUniformMatrix4fv(caches.program().getUniform("textureTransform"), 1, 349 GL_FALSE, &data.textureTransform.data[0]); 350 glUniform2fv(caches.program().getUniform("textureDimension"), 1, &data.textureDimension[0]); 351} 352 353void SkiaShader::store(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix, 354 GLuint* textureUnit, ProgramDescription* description, 355 SkiaShaderData* outData) { 356 if (tryStoreGradient(caches, shader, modelViewMatrix, 357 textureUnit, description, &outData->gradientData)) { 358 outData->skiaShaderType = kGradient_SkiaShaderType; 359 return; 360 } 361 362 if (tryStoreBitmap(caches, shader, modelViewMatrix, 363 textureUnit, description, &outData->bitmapData)) { 364 outData->skiaShaderType = kBitmap_SkiaShaderType; 365 return; 366 } 367 368 if (tryStoreCompose(caches, shader, modelViewMatrix, 369 textureUnit, description, outData)) { 370 outData->skiaShaderType = kCompose_SkiaShaderType; 371 return; 372 } 373 374 if (tryStoreLayer(caches, shader, modelViewMatrix, 375 textureUnit, description, &outData->layerData)) { 376 outData->skiaShaderType = kLayer_SkiaShaderType; 377 return; 378 } 379 380 // Unknown/unsupported type, so explicitly ignore shader 381 outData->skiaShaderType = kNone_SkiaShaderType; 382} 383 384void SkiaShader::apply(Caches& caches, const SkiaShaderData& data) { 385 if (!data.skiaShaderType) return; 386 387 if (data.skiaShaderType & kGradient_SkiaShaderType) { 388 applyGradient(caches, data.gradientData); 389 } 390 if (data.skiaShaderType & kBitmap_SkiaShaderType) { 391 applyBitmap(caches, data.bitmapData); 392 } 393 394 if (data.skiaShaderType == kLayer_SkiaShaderType) { 395 applyLayer(caches, data.layerData); 396 } 397} 398 399}; // namespace uirenderer 400}; // namespace android 401