SkiaShader.cpp revision 5a4690bf26932c0d6940e4af8516d920e09ae81a
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 outData->bitmapTexture = caches.textureCache.get(&bitmap); 205 if (!outData->bitmapTexture) return false; 206 207 outData->bitmapSampler = (*textureUnit)++; 208 209 const float width = outData->bitmapTexture->width; 210 const float height = outData->bitmapTexture->height; 211 212 description->hasBitmap = true; 213 if (!caches.extensions().hasNPot() 214 && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) 215 && (xy[0] != SkShader::kClamp_TileMode || xy[1] != SkShader::kClamp_TileMode)) { 216 description->isBitmapNpot = true; 217 description->bitmapWrapS = gTileModes[xy[0]]; 218 description->bitmapWrapT = gTileModes[xy[1]]; 219 220 outData->wrapS = GL_CLAMP_TO_EDGE; 221 outData->wrapT = GL_CLAMP_TO_EDGE; 222 } else { 223 outData->wrapS = gTileModes[xy[0]]; 224 outData->wrapT = gTileModes[xy[1]]; 225 } 226 227 computeScreenSpaceMatrix(outData->textureTransform, SkMatrix::I(), shader.getLocalMatrix(), 228 modelViewMatrix); 229 outData->textureDimension[0] = 1.0f / width; 230 outData->textureDimension[1] = 1.0f / height; 231 232 return true; 233} 234 235void applyBitmap(Caches& caches, const SkiaShaderData::BitmapShaderData& data) { 236 caches.textureState().activateTexture(data.bitmapSampler); 237 bindTexture(&caches, data.bitmapTexture, data.wrapS, data.wrapT); 238 data.bitmapTexture->setFilter(GL_LINEAR); 239 240 glUniform1i(caches.program().getUniform("bitmapSampler"), data.bitmapSampler); 241 glUniformMatrix4fv(caches.program().getUniform("textureTransform"), 1, GL_FALSE, 242 &data.textureTransform.data[0]); 243 glUniform2fv(caches.program().getUniform("textureDimension"), 1, &data.textureDimension[0]); 244} 245 246SkiaShaderType getComposeSubType(const SkShader& shader) { 247 // First check for a gradient shader. 248 switch (shader.asAGradient(nullptr)) { 249 case SkShader::kNone_GradientType: 250 // Not a gradient shader. Fall through to check for other types. 251 break; 252 case SkShader::kLinear_GradientType: 253 case SkShader::kRadial_GradientType: 254 case SkShader::kSweep_GradientType: 255 return kGradient_SkiaShaderType; 256 default: 257 // This is a Skia gradient that has no SkiaShader equivalent. Return None to skip. 258 return kNone_SkiaShaderType; 259 } 260 261 // The shader is not a gradient. Check for a bitmap shader. 262 if (shader.asABitmap(nullptr, nullptr, nullptr) == SkShader::kDefault_BitmapType) { 263 return kBitmap_SkiaShaderType; 264 } 265 return kNone_SkiaShaderType; 266} 267 268void storeCompose(Caches& caches, const SkShader& bitmapShader, const SkShader& gradientShader, 269 const Matrix4& modelViewMatrix, GLuint* textureUnit, 270 ProgramDescription* description, SkiaShaderData* outData) { 271 LOG_ALWAYS_FATAL_IF(!tryStoreBitmap(caches, bitmapShader, modelViewMatrix, 272 textureUnit, description, &outData->bitmapData), 273 "failed storing bitmap shader data"); 274 LOG_ALWAYS_FATAL_IF(!tryStoreGradient(caches, gradientShader, modelViewMatrix, 275 textureUnit, description, &outData->gradientData), 276 "failing storing gradient shader data"); 277} 278 279bool tryStoreCompose(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix, 280 GLuint* textureUnit, ProgramDescription* description, 281 SkiaShaderData* outData) { 282 283 SkShader::ComposeRec rec; 284 if (!shader.asACompose(&rec)) return false; 285 286 const SkiaShaderType shaderAType = getComposeSubType(*rec.fShaderA); 287 const SkiaShaderType shaderBType = getComposeSubType(*rec.fShaderB); 288 289 // check that type enum values are the 2 flags that compose the kCompose value 290 if ((shaderAType & shaderBType) != 0) return false; 291 if ((shaderAType | shaderBType) != kCompose_SkiaShaderType) return false; 292 293 mat4 transform; 294 computeScreenSpaceMatrix(transform, SkMatrix::I(), shader.getLocalMatrix(), modelViewMatrix); 295 if (shaderAType == kBitmap_SkiaShaderType) { 296 description->isBitmapFirst = true; 297 storeCompose(caches, *rec.fShaderA, *rec.fShaderB, 298 transform, textureUnit, description, outData); 299 } else { 300 description->isBitmapFirst = false; 301 storeCompose(caches, *rec.fShaderB, *rec.fShaderA, 302 transform, textureUnit, description, outData); 303 } 304 if (!SkXfermode::AsMode(rec.fMode, &description->shadersMode)) { 305 // TODO: Support other modes. 306 description->shadersMode = SkXfermode::kSrcOver_Mode; 307 } 308 return true; 309} 310 311bool tryStoreLayer(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix, 312 GLuint* textureUnit, ProgramDescription* description, 313 SkiaShaderData::LayerShaderData* outData) { 314 Layer* layer; 315 if (!shader.asACustomShader(reinterpret_cast<void**>(&layer))) { 316 return false; 317 } 318 319 description->hasBitmap = true; 320 outData->layer = layer; 321 outData->bitmapSampler = (*textureUnit)++; 322 323 const float width = layer->getWidth(); 324 const float height = layer->getHeight(); 325 326 computeScreenSpaceMatrix(outData->textureTransform, SkMatrix::I(), shader.getLocalMatrix(), 327 modelViewMatrix); 328 329 outData->textureDimension[0] = 1.0f / width; 330 outData->textureDimension[1] = 1.0f / height; 331 return true; 332} 333 334void applyLayer(Caches& caches, const SkiaShaderData::LayerShaderData& data) { 335 caches.textureState().activateTexture(data.bitmapSampler); 336 337 data.layer->bindTexture(); 338 data.layer->setWrap(GL_CLAMP_TO_EDGE); 339 data.layer->setFilter(GL_LINEAR); 340 341 glUniform1i(caches.program().getUniform("bitmapSampler"), data.bitmapSampler); 342 glUniformMatrix4fv(caches.program().getUniform("textureTransform"), 1, 343 GL_FALSE, &data.textureTransform.data[0]); 344 glUniform2fv(caches.program().getUniform("textureDimension"), 1, &data.textureDimension[0]); 345} 346 347void SkiaShader::store(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix, 348 GLuint* textureUnit, ProgramDescription* description, 349 SkiaShaderData* outData) { 350 if (tryStoreGradient(caches, shader, modelViewMatrix, 351 textureUnit, description, &outData->gradientData)) { 352 outData->skiaShaderType = kGradient_SkiaShaderType; 353 return; 354 } 355 356 if (tryStoreBitmap(caches, shader, modelViewMatrix, 357 textureUnit, description, &outData->bitmapData)) { 358 outData->skiaShaderType = kBitmap_SkiaShaderType; 359 return; 360 } 361 362 if (tryStoreCompose(caches, shader, modelViewMatrix, 363 textureUnit, description, outData)) { 364 outData->skiaShaderType = kCompose_SkiaShaderType; 365 return; 366 } 367 368 if (tryStoreLayer(caches, shader, modelViewMatrix, 369 textureUnit, description, &outData->layerData)) { 370 outData->skiaShaderType = kLayer_SkiaShaderType; 371 } 372} 373 374void SkiaShader::apply(Caches& caches, const SkiaShaderData& data) { 375 if (!data.skiaShaderType) return; 376 377 if (data.skiaShaderType & kGradient_SkiaShaderType) { 378 applyGradient(caches, data.gradientData); 379 } 380 if (data.skiaShaderType & kBitmap_SkiaShaderType) { 381 applyBitmap(caches, data.bitmapData); 382 } 383 384 if (data.skiaShaderType == kLayer_SkiaShaderType) { 385 applyLayer(caches, data.layerData); 386 } 387} 388 389}; // namespace uirenderer 390}; // namespace android 391