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,
    139                     gradInfo.fRadius[0], &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,
    154             shader.getLocalMatrix(), 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 
    186     if (CC_UNLIKELY(data.gradientTexture)) {
    187         caches.textureState().activateTexture(data.gradientSampler);
    188         bindTexture(&caches, data.gradientTexture, data.wrapST, data.wrapST);
    189         glUniform1i(caches.program().getUniform("gradientSampler"), data.gradientSampler);
    190     } else {
    191         bindUniformColor(caches.program().getUniform("startColor"), data.startColor);
    192         bindUniformColor(caches.program().getUniform("endColor"), data.endColor);
    193     }
    194 
    195     glUniform2f(caches.program().getUniform("screenSize"), 1.0f / width, 1.0f / height);
    196     glUniformMatrix4fv(caches.program().getUniform("screenSpace"), 1,
    197             GL_FALSE, &data.screenSpace.data[0]);
    198 }
    199 
    200 bool tryStoreBitmap(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
    201         GLuint* textureUnit, ProgramDescription* description,
    202         SkiaShaderData::BitmapShaderData* outData) {
    203     SkBitmap bitmap;
    204     SkShader::TileMode xy[2];
    205     if (!shader.isABitmap(&bitmap, nullptr, xy)) {
    206         return false;
    207     }
    208 
    209     // TODO: create  hwui-owned BitmapShader.
    210     Bitmap* hwuiBitmap = static_cast<Bitmap*>(bitmap.pixelRef());
    211     outData->bitmapTexture = caches.textureCache.get(hwuiBitmap);
    212     if (!outData->bitmapTexture) return false;
    213 
    214     outData->bitmapSampler = (*textureUnit)++;
    215 
    216     const float width = outData->bitmapTexture->width();
    217     const float height = outData->bitmapTexture->height();
    218 
    219     Texture* texture = outData->bitmapTexture;
    220 
    221     description->hasBitmap = true;
    222     description->hasLinearTexture = texture->isLinear();
    223     description->hasColorSpaceConversion = texture->hasColorSpaceConversion();
    224     description->transferFunction = texture->getTransferFunctionType();
    225     description->hasTranslucentConversion = texture->blend;
    226     description->isShaderBitmapExternal = hwuiBitmap->isHardware();
    227     // gralloc doesn't support non-clamp modes
    228     if (hwuiBitmap->isHardware() || (!caches.extensions().hasNPot()
    229             && (!isPowerOfTwo(width) || !isPowerOfTwo(height))
    230             && (xy[0] != SkShader::kClamp_TileMode || xy[1] != SkShader::kClamp_TileMode))) {
    231         // need non-clamp mode, but it's not supported for this draw,
    232         // so enable custom shader logic to mimic
    233         description->useShaderBasedWrap = true;
    234         description->bitmapWrapS = gTileModes[xy[0]];
    235         description->bitmapWrapT = gTileModes[xy[1]];
    236 
    237         outData->wrapS = GL_CLAMP_TO_EDGE;
    238         outData->wrapT = GL_CLAMP_TO_EDGE;
    239     } else {
    240         outData->wrapS = gTileModes[xy[0]];
    241         outData->wrapT = gTileModes[xy[1]];
    242     }
    243 
    244     computeScreenSpaceMatrix(outData->textureTransform, SkMatrix::I(), shader.getLocalMatrix(),
    245             modelViewMatrix);
    246     outData->textureDimension[0] = 1.0f / width;
    247     outData->textureDimension[1] = 1.0f / height;
    248 
    249     return true;
    250 }
    251 
    252 void applyBitmap(Caches& caches, const SkiaShaderData::BitmapShaderData& data) {
    253     caches.textureState().activateTexture(data.bitmapSampler);
    254     bindTexture(&caches, data.bitmapTexture, data.wrapS, data.wrapT);
    255     data.bitmapTexture->setFilter(GL_LINEAR);
    256 
    257     glUniform1i(caches.program().getUniform("bitmapSampler"), data.bitmapSampler);
    258     glUniformMatrix4fv(caches.program().getUniform("textureTransform"), 1, GL_FALSE,
    259             &data.textureTransform.data[0]);
    260     glUniform2fv(caches.program().getUniform("textureDimension"), 1, &data.textureDimension[0]);
    261 }
    262 
    263 SkiaShaderType getComposeSubType(const SkShader& shader) {
    264     // First check for a gradient shader.
    265     switch (shader.asAGradient(nullptr)) {
    266         case SkShader::kNone_GradientType:
    267             // Not a gradient shader. Fall through to check for other types.
    268             break;
    269         case SkShader::kLinear_GradientType:
    270         case SkShader::kRadial_GradientType:
    271         case SkShader::kSweep_GradientType:
    272             return kGradient_SkiaShaderType;
    273         default:
    274             // This is a Skia gradient that has no SkiaShader equivalent. Return None to skip.
    275             return kNone_SkiaShaderType;
    276     }
    277 
    278     // The shader is not a gradient. Check for a bitmap shader.
    279     if (shader.isABitmap()) {
    280         return kBitmap_SkiaShaderType;
    281     }
    282     return kNone_SkiaShaderType;
    283 }
    284 
    285 void storeCompose(Caches& caches, const SkShader& bitmapShader, const SkShader& gradientShader,
    286         const Matrix4& modelViewMatrix, GLuint* textureUnit,
    287         ProgramDescription* description, SkiaShaderData* outData) {
    288     LOG_ALWAYS_FATAL_IF(!tryStoreBitmap(caches, bitmapShader, modelViewMatrix,
    289                 textureUnit, description, &outData->bitmapData),
    290             "failed storing bitmap shader data");
    291     LOG_ALWAYS_FATAL_IF(!tryStoreGradient(caches, gradientShader, modelViewMatrix,
    292                 textureUnit, description, &outData->gradientData),
    293             "failing storing gradient shader data");
    294 }
    295 
    296 bool tryStoreCompose(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
    297         GLuint* textureUnit, ProgramDescription* description,
    298         SkiaShaderData* outData) {
    299 
    300     SkShader::ComposeRec rec;
    301     if (!shader.asACompose(&rec)) return false;
    302 
    303     const SkiaShaderType shaderAType = getComposeSubType(*rec.fShaderA);
    304     const SkiaShaderType shaderBType = getComposeSubType(*rec.fShaderB);
    305 
    306     // check that type enum values are the 2 flags that compose the kCompose value
    307     if ((shaderAType & shaderBType) != 0) return false;
    308     if ((shaderAType | shaderBType) != kCompose_SkiaShaderType) return false;
    309 
    310     mat4 transform;
    311     computeScreenSpaceMatrix(transform, SkMatrix::I(), shader.getLocalMatrix(), modelViewMatrix);
    312     if (shaderAType == kBitmap_SkiaShaderType) {
    313         description->isBitmapFirst = true;
    314         storeCompose(caches, *rec.fShaderA, *rec.fShaderB,
    315                 transform, textureUnit, description, outData);
    316     } else {
    317         description->isBitmapFirst = false;
    318         storeCompose(caches, *rec.fShaderB, *rec.fShaderA,
    319                 transform, textureUnit, description, outData);
    320     }
    321     description->shadersMode = rec.fBlendMode;
    322     return true;
    323 }
    324 
    325 void SkiaShader::store(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
    326         GLuint* textureUnit, ProgramDescription* description,
    327         SkiaShaderData* outData) {
    328     if (tryStoreGradient(caches, shader, modelViewMatrix,
    329             textureUnit, description, &outData->gradientData)) {
    330         outData->skiaShaderType = kGradient_SkiaShaderType;
    331         return;
    332     }
    333 
    334     if (tryStoreBitmap(caches, shader, modelViewMatrix,
    335             textureUnit, description, &outData->bitmapData)) {
    336         outData->skiaShaderType = kBitmap_SkiaShaderType;
    337         return;
    338     }
    339 
    340     if (tryStoreCompose(caches, shader, modelViewMatrix,
    341             textureUnit, description, outData)) {
    342         outData->skiaShaderType = kCompose_SkiaShaderType;
    343         return;
    344     }
    345 
    346     // Unknown/unsupported type, so explicitly ignore shader
    347     outData->skiaShaderType = kNone_SkiaShaderType;
    348 }
    349 
    350 void SkiaShader::apply(Caches& caches, const SkiaShaderData& data,
    351         const GLsizei width, const GLsizei height) {
    352     if (!data.skiaShaderType) return;
    353 
    354     if (data.skiaShaderType & kGradient_SkiaShaderType) {
    355         applyGradient(caches, data.gradientData, width, height);
    356     }
    357     if (data.skiaShaderType & kBitmap_SkiaShaderType) {
    358         applyBitmap(caches, data.bitmapData);
    359     }
    360 }
    361 
    362 }; // namespace uirenderer
    363 }; // namespace android
    364