Home | History | Annotate | Download | only in hwui
      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 "Matrix.h"
     22 #include "Texture.h"
     23 #include "hwui/Bitmap.h"
     24 
     25 #include <SkMatrix.h>
     26 #include <utils/Log.h>
     27 
     28 namespace android {
     29 namespace uirenderer {
     30 
     31 ///////////////////////////////////////////////////////////////////////////////
     32 // Support
     33 ///////////////////////////////////////////////////////////////////////////////
     34 
     35 static constexpr 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 static_assert(gTileModes[SkShader::kClamp_TileMode] == GL_CLAMP_TO_EDGE,
     42               "SkShader TileModes have changed");
     43 static_assert(gTileModes[SkShader::kRepeat_TileMode] == GL_REPEAT,
     44               "SkShader TileModes have changed");
     45 static_assert(gTileModes[SkShader::kMirror_TileMode] == GL_MIRRORED_REPEAT,
     46               "SkShader TileModes have changed");
     47 
     48 /**
     49  * This function does not work for n == 0.
     50  */
     51 static inline bool isPowerOfTwo(unsigned int n) {
     52     return !(n & (n - 1));
     53 }
     54 
     55 static inline void bindUniformColor(int slot, FloatColor color) {
     56     glUniform4fv(slot, 1, reinterpret_cast<const float*>(&color));
     57 }
     58 
     59 static inline void bindTexture(Caches* caches, Texture* texture, GLenum wrapS, GLenum wrapT) {
     60     caches->textureState().bindTexture(texture->target(), 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  */
     72 static 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 ///////////////////////////////////////////////////////////////////////////////
     83 // Gradient shader matrix helpers
     84 ///////////////////////////////////////////////////////////////////////////////
     85 
     86 static void toLinearUnitMatrix(const SkPoint pts[2], SkMatrix* matrix) {
     87     SkVector vec = pts[1] - pts[0];
     88     const float mag = vec.length();
     89     const float inv = mag ? 1.0f / mag : 0;
     90 
     91     vec.scale(inv);
     92     matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
     93     matrix->postTranslate(-pts[0].fX, -pts[0].fY);
     94     matrix->postScale(inv, inv);
     95 }
     96 
     97 static void toCircularUnitMatrix(const float x, const float y, const float radius,
     98                                  SkMatrix* matrix) {
     99     const float inv = 1.0f / radius;
    100     matrix->setTranslate(-x, -y);
    101     matrix->postScale(inv, inv);
    102 }
    103 
    104 static void toSweepUnitMatrix(const float x, const float y, SkMatrix* matrix) {
    105     matrix->setTranslate(-x, -y);
    106 }
    107 
    108 ///////////////////////////////////////////////////////////////////////////////
    109 // Common gradient code
    110 ///////////////////////////////////////////////////////////////////////////////
    111 
    112 static bool isSimpleGradient(const SkShader::GradientInfo& gradInfo) {
    113     return gradInfo.fColorCount == 2 && gradInfo.fTileMode == SkShader::kClamp_TileMode;
    114 }
    115 
    116 ///////////////////////////////////////////////////////////////////////////////
    117 // Store / apply
    118 ///////////////////////////////////////////////////////////////////////////////
    119 
    120 bool tryStoreGradient(Caches& caches, const SkShader& shader, const Matrix4 modelViewMatrix,
    121                       GLuint* textureUnit, ProgramDescription* description,
    122                       SkiaShaderData::GradientShaderData* outData) {
    123     SkShader::GradientInfo gradInfo;
    124     gradInfo.fColorCount = 0;
    125     gradInfo.fColors = nullptr;
    126     gradInfo.fColorOffsets = nullptr;
    127 
    128     SkMatrix unitMatrix;
    129     switch (shader.asAGradient(&gradInfo)) {
    130         case SkShader::kLinear_GradientType:
    131             description->gradientType = ProgramDescription::kGradientLinear;
    132 
    133             toLinearUnitMatrix(gradInfo.fPoint, &unitMatrix);
    134             break;
    135         case SkShader::kRadial_GradientType:
    136             description->gradientType = ProgramDescription::kGradientCircular;
    137 
    138             toCircularUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, gradInfo.fRadius[0],
    139                                  &unitMatrix);
    140             break;
    141         case SkShader::kSweep_GradientType:
    142             description->gradientType = ProgramDescription::kGradientSweep;
    143 
    144             toSweepUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, &unitMatrix);
    145             break;
    146         default:
    147             // Do nothing. This shader is unsupported.
    148             return false;
    149     }
    150     description->hasGradient = true;
    151     description->isSimpleGradient = isSimpleGradient(gradInfo);
    152 
    153     computeScreenSpaceMatrix(outData->screenSpace, unitMatrix, shader.getLocalMatrix(),
    154                              modelViewMatrix);
    155 
    156     // re-query shader to get full color / offset data
    157     std::unique_ptr<SkColor[]> colorStorage(new SkColor[gradInfo.fColorCount]);
    158     std::unique_ptr<SkScalar[]> colorOffsets(new SkScalar[gradInfo.fColorCount]);
    159     gradInfo.fColors = &colorStorage[0];
    160     gradInfo.fColorOffsets = &colorOffsets[0];
    161     shader.asAGradient(&gradInfo);
    162 
    163     if (CC_UNLIKELY(!description->isSimpleGradient)) {
    164         outData->gradientSampler = (*textureUnit)++;
    165 
    166 #ifndef SK_SCALAR_IS_FLOAT
    167 #error Need to convert gradInfo.fColorOffsets to float!
    168 #endif
    169         outData->gradientTexture = caches.gradientCache.get(
    170                 gradInfo.fColors, gradInfo.fColorOffsets, gradInfo.fColorCount);
    171         outData->wrapST = gTileModes[gradInfo.fTileMode];
    172     } else {
    173         outData->gradientSampler = 0;
    174         outData->gradientTexture = nullptr;
    175 
    176         outData->startColor.set(gradInfo.fColors[0]);
    177         outData->endColor.set(gradInfo.fColors[1]);
    178     }
    179 
    180     return true;
    181 }
    182 
    183 void applyGradient(Caches& caches, const SkiaShaderData::GradientShaderData& data,
    184                    const GLsizei width, const GLsizei height) {
    185     if (CC_UNLIKELY(data.gradientTexture)) {
    186         caches.textureState().activateTexture(data.gradientSampler);
    187         bindTexture(&caches, data.gradientTexture, data.wrapST, data.wrapST);
    188         glUniform1i(caches.program().getUniform("gradientSampler"), data.gradientSampler);
    189     } else {
    190         bindUniformColor(caches.program().getUniform("startColor"), data.startColor);
    191         bindUniformColor(caches.program().getUniform("endColor"), data.endColor);
    192     }
    193 
    194     glUniform2f(caches.program().getUniform("screenSize"), 1.0f / width, 1.0f / height);
    195     glUniformMatrix4fv(caches.program().getUniform("screenSpace"), 1, GL_FALSE,
    196                        &data.screenSpace.data[0]);
    197 }
    198 
    199 bool tryStoreBitmap(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
    200                     GLuint* textureUnit, ProgramDescription* description,
    201                     SkiaShaderData::BitmapShaderData* outData) {
    202     SkBitmap bitmap;
    203     SkShader::TileMode xy[2];
    204     if (!shader.isABitmap(&bitmap, nullptr, xy)) {
    205         return false;
    206     }
    207 
    208     // TODO: create  hwui-owned BitmapShader.
    209     Bitmap* hwuiBitmap = static_cast<Bitmap*>(bitmap.pixelRef());
    210     outData->bitmapTexture = caches.textureCache.get(hwuiBitmap);
    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     Texture* texture = outData->bitmapTexture;
    219 
    220     description->hasBitmap = true;
    221     description->hasLinearTexture = texture->isLinear();
    222     description->hasColorSpaceConversion = texture->hasColorSpaceConversion();
    223     description->transferFunction = texture->getTransferFunctionType();
    224     description->hasTranslucentConversion = texture->blend;
    225     description->isShaderBitmapExternal = hwuiBitmap->isHardware();
    226     // gralloc doesn't support non-clamp modes
    227     if (hwuiBitmap->isHardware() ||
    228         (!caches.extensions().hasNPot() && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) &&
    229          (xy[0] != SkShader::kClamp_TileMode || xy[1] != SkShader::kClamp_TileMode))) {
    230         // need non-clamp mode, but it's not supported for this draw,
    231         // so enable custom shader logic to mimic
    232         description->useShaderBasedWrap = true;
    233         description->bitmapWrapS = gTileModes[xy[0]];
    234         description->bitmapWrapT = gTileModes[xy[1]];
    235 
    236         outData->wrapS = GL_CLAMP_TO_EDGE;
    237         outData->wrapT = GL_CLAMP_TO_EDGE;
    238     } else {
    239         outData->wrapS = gTileModes[xy[0]];
    240         outData->wrapT = gTileModes[xy[1]];
    241     }
    242 
    243     computeScreenSpaceMatrix(outData->textureTransform, SkMatrix::I(), shader.getLocalMatrix(),
    244                              modelViewMatrix);
    245     outData->textureDimension[0] = 1.0f / width;
    246     outData->textureDimension[1] = 1.0f / height;
    247 
    248     return true;
    249 }
    250 
    251 void applyBitmap(Caches& caches, const SkiaShaderData::BitmapShaderData& data) {
    252     caches.textureState().activateTexture(data.bitmapSampler);
    253     bindTexture(&caches, data.bitmapTexture, data.wrapS, data.wrapT);
    254     data.bitmapTexture->setFilter(GL_LINEAR);
    255 
    256     glUniform1i(caches.program().getUniform("bitmapSampler"), data.bitmapSampler);
    257     glUniformMatrix4fv(caches.program().getUniform("textureTransform"), 1, GL_FALSE,
    258                        &data.textureTransform.data[0]);
    259     glUniform2fv(caches.program().getUniform("textureDimension"), 1, &data.textureDimension[0]);
    260 }
    261 
    262 SkiaShaderType getComposeSubType(const SkShader& shader) {
    263     // First check for a gradient shader.
    264     switch (shader.asAGradient(nullptr)) {
    265         case SkShader::kNone_GradientType:
    266             // Not a gradient shader. Fall through to check for other types.
    267             break;
    268         case SkShader::kLinear_GradientType:
    269         case SkShader::kRadial_GradientType:
    270         case SkShader::kSweep_GradientType:
    271             return kGradient_SkiaShaderType;
    272         default:
    273             // This is a Skia gradient that has no SkiaShader equivalent. Return None to skip.
    274             return kNone_SkiaShaderType;
    275     }
    276 
    277     // The shader is not a gradient. Check for a bitmap shader.
    278     if (shader.isABitmap()) {
    279         return kBitmap_SkiaShaderType;
    280     }
    281     return kNone_SkiaShaderType;
    282 }
    283 
    284 void storeCompose(Caches& caches, const SkShader& bitmapShader, const SkShader& gradientShader,
    285                   const Matrix4& modelViewMatrix, GLuint* textureUnit,
    286                   ProgramDescription* description, SkiaShaderData* outData) {
    287     LOG_ALWAYS_FATAL_IF(!tryStoreBitmap(caches, bitmapShader, modelViewMatrix, textureUnit,
    288                                         description, &outData->bitmapData),
    289                         "failed storing bitmap shader data");
    290     LOG_ALWAYS_FATAL_IF(!tryStoreGradient(caches, gradientShader, modelViewMatrix, textureUnit,
    291                                           description, &outData->gradientData),
    292                         "failing storing gradient shader data");
    293 }
    294 
    295 bool tryStoreCompose(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
    296                      GLuint* textureUnit, ProgramDescription* description,
    297                      SkiaShaderData* outData) {
    298     SkShader::ComposeRec rec;
    299     if (!shader.asACompose(&rec)) return false;
    300 
    301     const SkiaShaderType shaderAType = getComposeSubType(*rec.fShaderA);
    302     const SkiaShaderType shaderBType = getComposeSubType(*rec.fShaderB);
    303 
    304     // check that type enum values are the 2 flags that compose the kCompose value
    305     if ((shaderAType & shaderBType) != 0) return false;
    306     if ((shaderAType | shaderBType) != kCompose_SkiaShaderType) return false;
    307 
    308     mat4 transform;
    309     computeScreenSpaceMatrix(transform, SkMatrix::I(), shader.getLocalMatrix(), modelViewMatrix);
    310     if (shaderAType == kBitmap_SkiaShaderType) {
    311         description->isBitmapFirst = true;
    312         storeCompose(caches, *rec.fShaderA, *rec.fShaderB, transform, textureUnit, description,
    313                      outData);
    314     } else {
    315         description->isBitmapFirst = false;
    316         storeCompose(caches, *rec.fShaderB, *rec.fShaderA, transform, textureUnit, description,
    317                      outData);
    318     }
    319     description->shadersMode = rec.fBlendMode;
    320     return true;
    321 }
    322 
    323 void SkiaShader::store(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
    324                        GLuint* textureUnit, ProgramDescription* description,
    325                        SkiaShaderData* outData) {
    326     if (tryStoreGradient(caches, shader, modelViewMatrix, textureUnit, description,
    327                          &outData->gradientData)) {
    328         outData->skiaShaderType = kGradient_SkiaShaderType;
    329         return;
    330     }
    331 
    332     if (tryStoreBitmap(caches, shader, modelViewMatrix, textureUnit, description,
    333                        &outData->bitmapData)) {
    334         outData->skiaShaderType = kBitmap_SkiaShaderType;
    335         return;
    336     }
    337 
    338     if (tryStoreCompose(caches, shader, modelViewMatrix, textureUnit, description, outData)) {
    339         outData->skiaShaderType = kCompose_SkiaShaderType;
    340         return;
    341     }
    342 
    343     // Unknown/unsupported type, so explicitly ignore shader
    344     outData->skiaShaderType = kNone_SkiaShaderType;
    345 }
    346 
    347 void SkiaShader::apply(Caches& caches, const SkiaShaderData& data, const GLsizei width,
    348                        const GLsizei height) {
    349     if (!data.skiaShaderType) return;
    350 
    351     if (data.skiaShaderType & kGradient_SkiaShaderType) {
    352         applyGradient(caches, data.gradientData, width, height);
    353     }
    354     if (data.skiaShaderType & kBitmap_SkiaShaderType) {
    355         applyBitmap(caches, data.bitmapData);
    356     }
    357 }
    358 
    359 };  // namespace uirenderer
    360 };  // namespace android
    361