/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "OpenGLRenderer" #include "SkiaShader.h" #include "Caches.h" #include "Extensions.h" #include "Layer.h" #include "Matrix.h" #include "Texture.h" #include #include namespace android { namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// // Support /////////////////////////////////////////////////////////////////////////////// static const GLenum gTileModes[] = { GL_CLAMP_TO_EDGE, // == SkShader::kClamp_TileMode GL_REPEAT, // == SkShader::kRepeat_Mode GL_MIRRORED_REPEAT // == SkShader::kMirror_TileMode }; /** * This function does not work for n == 0. */ static inline bool isPowerOfTwo(unsigned int n) { return !(n & (n - 1)); } static inline void bindUniformColor(int slot, uint32_t color) { const float a = ((color >> 24) & 0xff) / 255.0f; glUniform4f(slot, a * ((color >> 16) & 0xff) / 255.0f, a * ((color >> 8) & 0xff) / 255.0f, a * ((color ) & 0xff) / 255.0f, a); } static inline void bindUniformColor(int slot, FloatColor color) { glUniform4fv(slot, 1, reinterpret_cast(&color)); } static inline void bindTexture(Caches* caches, Texture* texture, GLenum wrapS, GLenum wrapT) { caches->textureState().bindTexture(texture->id); texture->setWrapST(wrapS, wrapT); } /** * Compute the matrix to transform to screen space. * @param screenSpace Output param for the computed matrix. * @param unitMatrix The unit matrix for gradient shaders, as returned by SkShader::asAGradient, * or identity. * @param localMatrix Local matrix, as returned by SkShader::getLocalMatrix(). * @param modelViewMatrix Model view matrix, as supplied by the OpenGLRenderer. */ static void computeScreenSpaceMatrix(mat4& screenSpace, const SkMatrix& unitMatrix, const SkMatrix& localMatrix, const mat4& modelViewMatrix) { mat4 shaderMatrix; // uses implicit construction shaderMatrix.loadInverse(localMatrix); // again, uses implicit construction screenSpace.loadMultiply(unitMatrix, shaderMatrix); screenSpace.multiply(modelViewMatrix); } // Returns true if one is a bitmap and the other is a gradient static bool bitmapAndGradient(SkiaShaderType type1, SkiaShaderType type2) { return (type1 == kBitmap_SkiaShaderType && type2 == kGradient_SkiaShaderType) || (type2 == kBitmap_SkiaShaderType && type1 == kGradient_SkiaShaderType); } SkiaShaderType SkiaShader::getType(const SkShader& shader) { // First check for a gradient shader. switch (shader.asAGradient(nullptr)) { case SkShader::kNone_GradientType: // Not a gradient shader. Fall through to check for other types. break; case SkShader::kLinear_GradientType: case SkShader::kRadial_GradientType: case SkShader::kSweep_GradientType: return kGradient_SkiaShaderType; default: // This is a Skia gradient that has no SkiaShader equivalent. Return None to skip. return kNone_SkiaShaderType; } // The shader is not a gradient. Check for a bitmap shader. if (shader.asABitmap(nullptr, nullptr, nullptr) == SkShader::kDefault_BitmapType) { return kBitmap_SkiaShaderType; } // Check for a ComposeShader. SkShader::ComposeRec rec; if (shader.asACompose(&rec)) { const SkiaShaderType shaderAType = getType(*rec.fShaderA); const SkiaShaderType shaderBType = getType(*rec.fShaderB); // Compose is only supported if one is a bitmap and the other is a // gradient. Otherwise, return None to skip. if (!bitmapAndGradient(shaderAType, shaderBType)) { return kNone_SkiaShaderType; } return kCompose_SkiaShaderType; } if (shader.asACustomShader(nullptr)) { return kLayer_SkiaShaderType; } return kNone_SkiaShaderType; } typedef void (*describeProc)(Caches* caches, ProgramDescription& description, const Extensions& extensions, const SkShader& shader); describeProc gDescribeProc[] = { InvalidSkiaShader::describe, SkiaBitmapShader::describe, SkiaGradientShader::describe, SkiaComposeShader::describe, SkiaLayerShader::describe, }; typedef void (*setupProgramProc)(Caches* caches, const mat4& modelViewMatrix, GLuint* textureUnit, const Extensions& extensions, const SkShader& shader); setupProgramProc gSetupProgramProc[] = { InvalidSkiaShader::setupProgram, SkiaBitmapShader::setupProgram, SkiaGradientShader::setupProgram, SkiaComposeShader::setupProgram, SkiaLayerShader::setupProgram, }; void SkiaShader::describe(Caches* caches, ProgramDescription& description, const Extensions& extensions, const SkShader& shader) { gDescribeProc[getType(shader)](caches, description, extensions, shader); } void SkiaShader::setupProgram(Caches* caches, const mat4& modelViewMatrix, GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) { gSetupProgramProc[getType(shader)](caches, modelViewMatrix, textureUnit, extensions, shader); } /////////////////////////////////////////////////////////////////////////////// // Layer shader /////////////////////////////////////////////////////////////////////////////// void SkiaLayerShader::describe(Caches*, ProgramDescription& description, const Extensions&, const SkShader& shader) { description.hasBitmap = true; } void SkiaLayerShader::setupProgram(Caches* caches, const mat4& modelViewMatrix, GLuint* textureUnit, const Extensions&, const SkShader& shader) { Layer* layer; if (!shader.asACustomShader(reinterpret_cast(&layer))) { LOG_ALWAYS_FATAL("SkiaLayerShader::setupProgram called on the wrong type of shader!"); } GLuint textureSlot = (*textureUnit)++; caches->textureState().activateTexture(textureSlot); const float width = layer->getWidth(); const float height = layer->getHeight(); mat4 textureTransform; computeScreenSpaceMatrix(textureTransform, SkMatrix::I(), shader.getLocalMatrix(), modelViewMatrix); // Uniforms layer->bindTexture(); layer->setWrap(GL_CLAMP_TO_EDGE); layer->setFilter(GL_LINEAR); Program& program = caches->program(); glUniform1i(program.getUniform("bitmapSampler"), textureSlot); glUniformMatrix4fv(program.getUniform("textureTransform"), 1, GL_FALSE, &textureTransform.data[0]); glUniform2f(program.getUniform("textureDimension"), 1.0f / width, 1.0f / height); } /////////////////////////////////////////////////////////////////////////////// // Bitmap shader /////////////////////////////////////////////////////////////////////////////// struct BitmapShaderInfo { float width; float height; GLenum wrapS; GLenum wrapT; Texture* texture; }; static bool bitmapShaderHelper(Caches* caches, ProgramDescription* description, BitmapShaderInfo* shaderInfo, const Extensions& extensions, const SkBitmap& bitmap, SkShader::TileMode tileModes[2]) { Texture* texture = caches->textureCache.get(&bitmap); if (!texture) return false; const float width = texture->width; const float height = texture->height; GLenum wrapS, wrapT; if (description) { description->hasBitmap = true; } // The driver does not support non-power of two mirrored/repeated // textures, so do it ourselves if (!extensions.hasNPot() && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) && (tileModes[0] != SkShader::kClamp_TileMode || tileModes[1] != SkShader::kClamp_TileMode)) { if (description) { description->isBitmapNpot = true; description->bitmapWrapS = gTileModes[tileModes[0]]; description->bitmapWrapT = gTileModes[tileModes[1]]; } wrapS = GL_CLAMP_TO_EDGE; wrapT = GL_CLAMP_TO_EDGE; } else { wrapS = gTileModes[tileModes[0]]; wrapT = gTileModes[tileModes[1]]; } if (shaderInfo) { shaderInfo->width = width; shaderInfo->height = height; shaderInfo->wrapS = wrapS; shaderInfo->wrapT = wrapT; shaderInfo->texture = texture; } return true; } void SkiaBitmapShader::describe(Caches* caches, ProgramDescription& description, const Extensions& extensions, const SkShader& shader) { SkBitmap bitmap; SkShader::TileMode xy[2]; if (shader.asABitmap(&bitmap, nullptr, xy) != SkShader::kDefault_BitmapType) { LOG_ALWAYS_FATAL("SkiaBitmapShader::describe called with a different kind of shader!"); } bitmapShaderHelper(caches, &description, nullptr, extensions, bitmap, xy); } void SkiaBitmapShader::setupProgram(Caches* caches, const mat4& modelViewMatrix, GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) { SkBitmap bitmap; SkShader::TileMode xy[2]; if (shader.asABitmap(&bitmap, nullptr, xy) != SkShader::kDefault_BitmapType) { LOG_ALWAYS_FATAL("SkiaBitmapShader::setupProgram called with a different kind of shader!"); } GLuint textureSlot = (*textureUnit)++; caches->textureState().activateTexture(textureSlot); BitmapShaderInfo shaderInfo; if (!bitmapShaderHelper(caches, nullptr, &shaderInfo, extensions, bitmap, xy)) { return; } Program& program = caches->program(); Texture* texture = shaderInfo.texture; const AutoTexture autoCleanup(texture); mat4 textureTransform; computeScreenSpaceMatrix(textureTransform, SkMatrix::I(), shader.getLocalMatrix(), modelViewMatrix); // Uniforms bindTexture(caches, texture, shaderInfo.wrapS, shaderInfo.wrapT); texture->setFilter(GL_LINEAR); glUniform1i(program.getUniform("bitmapSampler"), textureSlot); glUniformMatrix4fv(program.getUniform("textureTransform"), 1, GL_FALSE, &textureTransform.data[0]); glUniform2f(program.getUniform("textureDimension"), 1.0f / shaderInfo.width, 1.0f / shaderInfo.height); } /////////////////////////////////////////////////////////////////////////////// // Linear gradient shader /////////////////////////////////////////////////////////////////////////////// static void toUnitMatrix(const SkPoint pts[2], SkMatrix* matrix) { SkVector vec = pts[1] - pts[0]; const float mag = vec.length(); const float inv = mag ? 1.0f / mag : 0; vec.scale(inv); matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY); matrix->postTranslate(-pts[0].fX, -pts[0].fY); matrix->postScale(inv, inv); } /////////////////////////////////////////////////////////////////////////////// // Circular gradient shader /////////////////////////////////////////////////////////////////////////////// static void toCircularUnitMatrix(const float x, const float y, const float radius, SkMatrix* matrix) { const float inv = 1.0f / radius; matrix->setTranslate(-x, -y); matrix->postScale(inv, inv); } /////////////////////////////////////////////////////////////////////////////// // Sweep gradient shader /////////////////////////////////////////////////////////////////////////////// static void toSweepUnitMatrix(const float x, const float y, SkMatrix* matrix) { matrix->setTranslate(-x, -y); } /////////////////////////////////////////////////////////////////////////////// // Common gradient code /////////////////////////////////////////////////////////////////////////////// static bool isSimpleGradient(const SkShader::GradientInfo& gradInfo) { return gradInfo.fColorCount == 2 && gradInfo.fTileMode == SkShader::kClamp_TileMode; } void SkiaGradientShader::describe(Caches*, ProgramDescription& description, const Extensions& extensions, const SkShader& shader) { SkShader::GradientInfo gradInfo; gradInfo.fColorCount = 0; gradInfo.fColors = nullptr; gradInfo.fColorOffsets = nullptr; switch (shader.asAGradient(&gradInfo)) { case SkShader::kLinear_GradientType: description.gradientType = ProgramDescription::kGradientLinear; break; case SkShader::kRadial_GradientType: description.gradientType = ProgramDescription::kGradientCircular; break; case SkShader::kSweep_GradientType: description.gradientType = ProgramDescription::kGradientSweep; break; default: // Do nothing. This shader is unsupported. return; } description.hasGradient = true; description.isSimpleGradient = isSimpleGradient(gradInfo); } void SkiaGradientShader::setupProgram(Caches* caches, const mat4& modelViewMatrix, GLuint* textureUnit, const Extensions&, const SkShader& shader) { // SkShader::GradientInfo.fColorCount is an in/out parameter. As input, it tells asAGradient // how much space has been allocated for fColors and fColorOffsets. 10 was chosen // arbitrarily, but should be >= 2. // As output, it tells the number of actual colors/offsets in the gradient. const int COLOR_COUNT = 10; SkAutoSTMalloc colorStorage(COLOR_COUNT); SkAutoSTMalloc positionStorage(COLOR_COUNT); SkShader::GradientInfo gradInfo; gradInfo.fColorCount = COLOR_COUNT; gradInfo.fColors = colorStorage.get(); gradInfo.fColorOffsets = positionStorage.get(); SkShader::GradientType gradType = shader.asAGradient(&gradInfo); Program& program = caches->program(); if (CC_UNLIKELY(!isSimpleGradient(gradInfo))) { if (gradInfo.fColorCount > COLOR_COUNT) { // There was not enough room in our arrays for all the colors and offsets. Try again, // now that we know the true number of colors. gradInfo.fColors = colorStorage.reset(gradInfo.fColorCount); gradInfo.fColorOffsets = positionStorage.reset(gradInfo.fColorCount); shader.asAGradient(&gradInfo); } GLuint textureSlot = (*textureUnit)++; caches->textureState().activateTexture(textureSlot); #ifndef SK_SCALAR_IS_FLOAT #error Need to convert gradInfo.fColorOffsets to float! #endif Texture* texture = caches->gradientCache.get(gradInfo.fColors, gradInfo.fColorOffsets, gradInfo.fColorCount); // Uniforms bindTexture(caches, texture, gTileModes[gradInfo.fTileMode], gTileModes[gradInfo.fTileMode]); glUniform1i(program.getUniform("gradientSampler"), textureSlot); } else { bindUniformColor(program.getUniform("startColor"), gradInfo.fColors[0]); bindUniformColor(program.getUniform("endColor"), gradInfo.fColors[1]); } caches->dither.setupProgram(program, textureUnit); SkMatrix unitMatrix; switch (gradType) { case SkShader::kLinear_GradientType: toUnitMatrix(gradInfo.fPoint, &unitMatrix); break; case SkShader::kRadial_GradientType: toCircularUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, gradInfo.fRadius[0], &unitMatrix); break; case SkShader::kSweep_GradientType: toSweepUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, &unitMatrix); break; default: LOG_ALWAYS_FATAL("Invalid SkShader gradient type %d", gradType); } mat4 screenSpace; computeScreenSpaceMatrix(screenSpace, unitMatrix, shader.getLocalMatrix(), modelViewMatrix); glUniformMatrix4fv(program.getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]); } /////////////////////////////////////////////////////////////////////////////// // Compose shader /////////////////////////////////////////////////////////////////////////////// void SkiaComposeShader::describe(Caches* caches, ProgramDescription& description, const Extensions& extensions, const SkShader& shader) { SkShader::ComposeRec rec; if (!shader.asACompose(&rec)) { LOG_ALWAYS_FATAL("SkiaComposeShader::describe called on the wrong shader type!"); } SkiaShader::describe(caches, description, extensions, *rec.fShaderA); SkiaShader::describe(caches, description, extensions, *rec.fShaderB); if (SkiaShader::getType(*rec.fShaderA) == kBitmap_SkiaShaderType) { description.isBitmapFirst = true; } if (!SkXfermode::AsMode(rec.fMode, &description.shadersMode)) { // TODO: Support other modes. description.shadersMode = SkXfermode::kSrcOver_Mode; } } void SkiaComposeShader::setupProgram(Caches* caches, const mat4& modelViewMatrix, GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) { SkShader::ComposeRec rec; if (!shader.asACompose(&rec)) { LOG_ALWAYS_FATAL("SkiaComposeShader::setupProgram called on the wrong shader type!"); } // Apply this compose shader's local transform and pass it down to // the child shaders. They will in turn apply their local transform // to this matrix. mat4 transform; computeScreenSpaceMatrix(transform, SkMatrix::I(), shader.getLocalMatrix(), modelViewMatrix); SkiaShader::setupProgram(caches, transform, textureUnit, extensions, *rec.fShaderA); SkiaShader::setupProgram(caches, transform, textureUnit, extensions, *rec.fShaderB); } /////////////////////////////////////////////////////////////////////////////// // Store / apply /////////////////////////////////////////////////////////////////////////////// bool tryStoreGradient(Caches& caches, const SkShader& shader, const Matrix4 modelViewMatrix, GLuint* textureUnit, ProgramDescription* description, SkiaShaderData::GradientShaderData* outData) { SkShader::GradientInfo gradInfo; gradInfo.fColorCount = 0; gradInfo.fColors = nullptr; gradInfo.fColorOffsets = nullptr; SkMatrix unitMatrix; switch (shader.asAGradient(&gradInfo)) { case SkShader::kLinear_GradientType: description->gradientType = ProgramDescription::kGradientLinear; toUnitMatrix(gradInfo.fPoint, &unitMatrix); break; case SkShader::kRadial_GradientType: description->gradientType = ProgramDescription::kGradientCircular; toCircularUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, gradInfo.fRadius[0], &unitMatrix); break; case SkShader::kSweep_GradientType: description->gradientType = ProgramDescription::kGradientSweep; toSweepUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, &unitMatrix); break; default: // Do nothing. This shader is unsupported. return false; } description->hasGradient = true; description->isSimpleGradient = isSimpleGradient(gradInfo); computeScreenSpaceMatrix(outData->screenSpace, unitMatrix, shader.getLocalMatrix(), modelViewMatrix); // re-query shader to get full color / offset data std::unique_ptr colorStorage(new SkColor[gradInfo.fColorCount]); std::unique_ptr colorOffsets(new SkScalar[gradInfo.fColorCount]); gradInfo.fColors = &colorStorage[0]; gradInfo.fColorOffsets = &colorOffsets[0]; shader.asAGradient(&gradInfo); if (CC_UNLIKELY(!isSimpleGradient(gradInfo))) { outData->gradientSampler = (*textureUnit)++; #ifndef SK_SCALAR_IS_FLOAT #error Need to convert gradInfo.fColorOffsets to float! #endif outData->gradientTexture = caches.gradientCache.get( gradInfo.fColors, gradInfo.fColorOffsets, gradInfo.fColorCount); outData->wrapST = gTileModes[gradInfo.fTileMode]; } else { outData->gradientSampler = 0; outData->gradientTexture = nullptr; outData->startColor.set(gradInfo.fColors[0]); outData->endColor.set(gradInfo.fColors[1]); } outData->ditherSampler = (*textureUnit)++; return true; } void applyGradient(Caches& caches, const SkiaShaderData::GradientShaderData& data) { if (CC_UNLIKELY(data.gradientTexture)) { caches.textureState().activateTexture(data.gradientSampler); bindTexture(&caches, data.gradientTexture, data.wrapST, data.wrapST); glUniform1i(caches.program().getUniform("gradientSampler"), data.gradientSampler); } else { bindUniformColor(caches.program().getUniform("startColor"), data.startColor); bindUniformColor(caches.program().getUniform("endColor"), data.endColor); } // TODO: remove sampler slot incrementing from dither.setupProgram, // since this assignment of slots is done at store, not apply time GLuint ditherSampler = data.ditherSampler; caches.dither.setupProgram(caches.program(), &ditherSampler); glUniformMatrix4fv(caches.program().getUniform("screenSpace"), 1, GL_FALSE, &data.screenSpace.data[0]); } bool tryStoreBitmap(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix, GLuint* textureUnit, ProgramDescription* description, SkiaShaderData::BitmapShaderData* outData) { SkBitmap bitmap; SkShader::TileMode xy[2]; if (shader.asABitmap(&bitmap, nullptr, xy) != SkShader::kDefault_BitmapType) { return false; } outData->bitmapTexture = caches.textureCache.get(&bitmap); if (!outData->bitmapTexture) return false; outData->bitmapSampler = (*textureUnit)++; const float width = outData->bitmapTexture->width; const float height = outData->bitmapTexture->height; description->hasBitmap = true; if (!caches.extensions().hasNPot() && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) && (xy[0] != SkShader::kClamp_TileMode || xy[1] != SkShader::kClamp_TileMode)) { description->isBitmapNpot = true; description->bitmapWrapS = gTileModes[xy[0]]; description->bitmapWrapT = gTileModes[xy[1]]; outData->wrapS = GL_CLAMP_TO_EDGE; outData->wrapT = GL_CLAMP_TO_EDGE; } else { outData->wrapS = gTileModes[xy[0]]; outData->wrapT = gTileModes[xy[1]]; } computeScreenSpaceMatrix(outData->textureTransform, SkMatrix::I(), shader.getLocalMatrix(), modelViewMatrix); outData->textureDimension[0] = 1.0f / width; outData->textureDimension[1] = 1.0f / height; return true; } void applyBitmap(Caches& caches, const SkiaShaderData::BitmapShaderData& data) { caches.textureState().activateTexture(data.bitmapSampler); bindTexture(&caches, data.bitmapTexture, data.wrapS, data.wrapT); data.bitmapTexture->setFilter(GL_LINEAR); glUniform1i(caches.program().getUniform("bitmapSampler"), data.bitmapSampler); glUniformMatrix4fv(caches.program().getUniform("textureTransform"), 1, GL_FALSE, &data.textureTransform.data[0]); glUniform2fv(caches.program().getUniform("textureDimension"), 1, &data.textureDimension[0]); } SkiaShaderType getComposeSubType(const SkShader& shader) { // First check for a gradient shader. switch (shader.asAGradient(nullptr)) { case SkShader::kNone_GradientType: // Not a gradient shader. Fall through to check for other types. break; case SkShader::kLinear_GradientType: case SkShader::kRadial_GradientType: case SkShader::kSweep_GradientType: return kGradient_SkiaShaderType; default: // This is a Skia gradient that has no SkiaShader equivalent. Return None to skip. return kNone_SkiaShaderType; } // The shader is not a gradient. Check for a bitmap shader. if (shader.asABitmap(nullptr, nullptr, nullptr) == SkShader::kDefault_BitmapType) { return kBitmap_SkiaShaderType; } return kNone_SkiaShaderType; } void storeCompose(Caches& caches, const SkShader& bitmapShader, const SkShader& gradientShader, const Matrix4& modelViewMatrix, GLuint* textureUnit, ProgramDescription* description, SkiaShaderData* outData) { LOG_ALWAYS_FATAL_IF(!tryStoreBitmap(caches, bitmapShader, modelViewMatrix, textureUnit, description, &outData->bitmapData), "failed storing bitmap shader data"); LOG_ALWAYS_FATAL_IF(!tryStoreGradient(caches, gradientShader, modelViewMatrix, textureUnit, description, &outData->gradientData), "failing storing gradient shader data"); } bool tryStoreCompose(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix, GLuint* textureUnit, ProgramDescription* description, SkiaShaderData* outData) { SkShader::ComposeRec rec; if (!shader.asACompose(&rec)) return false; const SkiaShaderType shaderAType = getComposeSubType(*rec.fShaderA); const SkiaShaderType shaderBType = getComposeSubType(*rec.fShaderB); // check that type enum values are the 2 flags that compose the kCompose value if ((shaderAType & shaderBType) != 0) return false; if ((shaderAType | shaderBType) != kCompose_SkiaShaderType) return false; mat4 transform; computeScreenSpaceMatrix(transform, SkMatrix::I(), shader.getLocalMatrix(), modelViewMatrix); if (shaderAType == kBitmap_SkiaShaderType) { description->isBitmapFirst = true; storeCompose(caches, *rec.fShaderA, *rec.fShaderB, transform, textureUnit, description, outData); } else { description->isBitmapFirst = false; storeCompose(caches, *rec.fShaderB, *rec.fShaderA, transform, textureUnit, description, outData); } if (!SkXfermode::AsMode(rec.fMode, &description->shadersMode)) { // TODO: Support other modes. description->shadersMode = SkXfermode::kSrcOver_Mode; } return true; } bool tryStoreLayer(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix, GLuint* textureUnit, ProgramDescription* description, SkiaShaderData::LayerShaderData* outData) { Layer* layer; if (!shader.asACustomShader(reinterpret_cast(&layer))) { return false; } description->hasBitmap = true; outData->layer = layer; outData->bitmapSampler = (*textureUnit)++; const float width = layer->getWidth(); const float height = layer->getHeight(); computeScreenSpaceMatrix(outData->textureTransform, SkMatrix::I(), shader.getLocalMatrix(), modelViewMatrix); outData->textureDimension[0] = 1.0f / width; outData->textureDimension[1] = 1.0f / height; return true; } void applyLayer(Caches& caches, const SkiaShaderData::LayerShaderData& data) { caches.textureState().activateTexture(data.bitmapSampler); data.layer->bindTexture(); data.layer->setWrap(GL_CLAMP_TO_EDGE); data.layer->setFilter(GL_LINEAR); glUniform1i(caches.program().getUniform("bitmapSampler"), data.bitmapSampler); glUniformMatrix4fv(caches.program().getUniform("textureTransform"), 1, GL_FALSE, &data.textureTransform.data[0]); glUniform2fv(caches.program().getUniform("textureDimension"), 1, &data.textureDimension[0]); } void SkiaShader::store(Caches& caches, const SkShader* shader, const Matrix4& modelViewMatrix, GLuint* textureUnit, ProgramDescription* description, SkiaShaderData* outData) { if (!shader) { outData->skiaShaderType = kNone_SkiaShaderType; return; } if (tryStoreGradient(caches, *shader, modelViewMatrix, textureUnit, description, &outData->gradientData)) { outData->skiaShaderType = kGradient_SkiaShaderType; return; } if (tryStoreBitmap(caches, *shader, modelViewMatrix, textureUnit, description, &outData->bitmapData)) { outData->skiaShaderType = kBitmap_SkiaShaderType; return; } if (tryStoreCompose(caches, *shader, modelViewMatrix, textureUnit, description, outData)) { outData->skiaShaderType = kCompose_SkiaShaderType; return; } if (tryStoreLayer(caches, *shader, modelViewMatrix, textureUnit, description, &outData->layerData)) { outData->skiaShaderType = kLayer_SkiaShaderType; } } void SkiaShader::apply(Caches& caches, const SkiaShaderData& data) { if (!data.skiaShaderType) return; if (data.skiaShaderType & kGradient_SkiaShaderType) { applyGradient(caches, data.gradientData); } if (data.skiaShaderType & kBitmap_SkiaShaderType) { applyBitmap(caches, data.bitmapData); } if (data.skiaShaderType == kLayer_SkiaShaderType) { applyLayer(caches, data.layerData); } } }; // namespace uirenderer }; // namespace android