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 "Layer.h"
     22 #include "Matrix.h"
     23 #include "Texture.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->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(!isSimpleGradient(gradInfo))) {
    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     outData->ditherSampler = (*textureUnit)++;
    181     return true;
    182 }
    183 
    184 void applyGradient(Caches& caches, const SkiaShaderData::GradientShaderData& data) {
    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     // TODO: remove sampler slot incrementing from dither.setupProgram,
    195     // since this assignment of slots is done at store, not apply time
    196     GLuint ditherSampler = data.ditherSampler;
    197     caches.dither.setupProgram(caches.program(), &ditherSampler);
    198     glUniformMatrix4fv(caches.program().getUniform("screenSpace"), 1,
    199             GL_FALSE, &data.screenSpace.data[0]);
    200 }
    201 
    202 bool tryStoreBitmap(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
    203         GLuint* textureUnit, ProgramDescription* description,
    204         SkiaShaderData::BitmapShaderData* outData) {
    205     SkBitmap bitmap;
    206     SkShader::TileMode xy[2];
    207     if (!shader.isABitmap(&bitmap, nullptr, xy)) {
    208         return false;
    209     }
    210 
    211     /*
    212      * Bypass the AssetAtlas, since those textures:
    213      * 1) require UV mapping, which isn't implemented in matrix computation below
    214      * 2) can't handle REPEAT simply
    215      * 3) are safe to upload here (outside of sync stage), since they're static
    216      */
    217     outData->bitmapTexture = caches.textureCache.getAndBypassAtlas(&bitmap);
    218     if (!outData->bitmapTexture) return false;
    219 
    220     outData->bitmapSampler = (*textureUnit)++;
    221 
    222     const float width = outData->bitmapTexture->width();
    223     const float height = outData->bitmapTexture->height();
    224 
    225     description->hasBitmap = true;
    226     if (!caches.extensions().hasNPot()
    227             && (!isPowerOfTwo(width) || !isPowerOfTwo(height))
    228             && (xy[0] != SkShader::kClamp_TileMode || xy[1] != SkShader::kClamp_TileMode)) {
    229         description->isBitmapNpot = true;
    230         description->bitmapWrapS = gTileModes[xy[0]];
    231         description->bitmapWrapT = gTileModes[xy[1]];
    232 
    233         outData->wrapS = GL_CLAMP_TO_EDGE;
    234         outData->wrapT = GL_CLAMP_TO_EDGE;
    235     } else {
    236         outData->wrapS = gTileModes[xy[0]];
    237         outData->wrapT = gTileModes[xy[1]];
    238     }
    239 
    240     computeScreenSpaceMatrix(outData->textureTransform, SkMatrix::I(), shader.getLocalMatrix(),
    241             modelViewMatrix);
    242     outData->textureDimension[0] = 1.0f / width;
    243     outData->textureDimension[1] = 1.0f / height;
    244 
    245     return true;
    246 }
    247 
    248 void applyBitmap(Caches& caches, const SkiaShaderData::BitmapShaderData& data) {
    249     caches.textureState().activateTexture(data.bitmapSampler);
    250     bindTexture(&caches, data.bitmapTexture, data.wrapS, data.wrapT);
    251     data.bitmapTexture->setFilter(GL_LINEAR);
    252 
    253     glUniform1i(caches.program().getUniform("bitmapSampler"), data.bitmapSampler);
    254     glUniformMatrix4fv(caches.program().getUniform("textureTransform"), 1, GL_FALSE,
    255             &data.textureTransform.data[0]);
    256     glUniform2fv(caches.program().getUniform("textureDimension"), 1, &data.textureDimension[0]);
    257 }
    258 
    259 SkiaShaderType getComposeSubType(const SkShader& shader) {
    260     // First check for a gradient shader.
    261     switch (shader.asAGradient(nullptr)) {
    262         case SkShader::kNone_GradientType:
    263             // Not a gradient shader. Fall through to check for other types.
    264             break;
    265         case SkShader::kLinear_GradientType:
    266         case SkShader::kRadial_GradientType:
    267         case SkShader::kSweep_GradientType:
    268             return kGradient_SkiaShaderType;
    269         default:
    270             // This is a Skia gradient that has no SkiaShader equivalent. Return None to skip.
    271             return kNone_SkiaShaderType;
    272     }
    273 
    274     // The shader is not a gradient. Check for a bitmap shader.
    275     if (shader.isABitmap()) {
    276         return kBitmap_SkiaShaderType;
    277     }
    278     return kNone_SkiaShaderType;
    279 }
    280 
    281 void storeCompose(Caches& caches, const SkShader& bitmapShader, const SkShader& gradientShader,
    282         const Matrix4& modelViewMatrix, GLuint* textureUnit,
    283         ProgramDescription* description, SkiaShaderData* outData) {
    284     LOG_ALWAYS_FATAL_IF(!tryStoreBitmap(caches, bitmapShader, modelViewMatrix,
    285                 textureUnit, description, &outData->bitmapData),
    286             "failed storing bitmap shader data");
    287     LOG_ALWAYS_FATAL_IF(!tryStoreGradient(caches, gradientShader, modelViewMatrix,
    288                 textureUnit, description, &outData->gradientData),
    289             "failing storing gradient shader data");
    290 }
    291 
    292 bool tryStoreCompose(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
    293         GLuint* textureUnit, ProgramDescription* description,
    294         SkiaShaderData* outData) {
    295 
    296     SkShader::ComposeRec rec;
    297     if (!shader.asACompose(&rec)) return false;
    298 
    299     const SkiaShaderType shaderAType = getComposeSubType(*rec.fShaderA);
    300     const SkiaShaderType shaderBType = getComposeSubType(*rec.fShaderB);
    301 
    302     // check that type enum values are the 2 flags that compose the kCompose value
    303     if ((shaderAType & shaderBType) != 0) return false;
    304     if ((shaderAType | shaderBType) != kCompose_SkiaShaderType) return false;
    305 
    306     mat4 transform;
    307     computeScreenSpaceMatrix(transform, SkMatrix::I(), shader.getLocalMatrix(), modelViewMatrix);
    308     if (shaderAType == kBitmap_SkiaShaderType) {
    309         description->isBitmapFirst = true;
    310         storeCompose(caches, *rec.fShaderA, *rec.fShaderB,
    311                 transform, textureUnit, description, outData);
    312     } else {
    313         description->isBitmapFirst = false;
    314         storeCompose(caches, *rec.fShaderB, *rec.fShaderA,
    315                 transform, textureUnit, description, outData);
    316     }
    317     if (!SkXfermode::AsMode(rec.fMode, &description->shadersMode)) {
    318         // TODO: Support other modes.
    319         description->shadersMode = SkXfermode::kSrcOver_Mode;
    320     }
    321     return true;
    322 }
    323 
    324 bool tryStoreLayer(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
    325         GLuint* textureUnit, ProgramDescription* description,
    326         SkiaShaderData::LayerShaderData* outData) {
    327     Layer* layer;
    328     if (!shader.asACustomShader(reinterpret_cast<void**>(&layer))) {
    329         return false;
    330     }
    331 
    332     description->hasBitmap = true;
    333     outData->layer = layer;
    334     outData->bitmapSampler = (*textureUnit)++;
    335 
    336     const float width = layer->getWidth();
    337     const float height = layer->getHeight();
    338 
    339     computeScreenSpaceMatrix(outData->textureTransform, SkMatrix::I(), shader.getLocalMatrix(),
    340             modelViewMatrix);
    341 
    342     outData->textureDimension[0] = 1.0f / width;
    343     outData->textureDimension[1] = 1.0f / height;
    344     return true;
    345 }
    346 
    347 void applyLayer(Caches& caches, const SkiaShaderData::LayerShaderData& data) {
    348     caches.textureState().activateTexture(data.bitmapSampler);
    349 
    350     data.layer->bindTexture();
    351     data.layer->setWrap(GL_CLAMP_TO_EDGE);
    352     data.layer->setFilter(GL_LINEAR);
    353 
    354     glUniform1i(caches.program().getUniform("bitmapSampler"), data.bitmapSampler);
    355     glUniformMatrix4fv(caches.program().getUniform("textureTransform"), 1,
    356             GL_FALSE, &data.textureTransform.data[0]);
    357     glUniform2fv(caches.program().getUniform("textureDimension"), 1, &data.textureDimension[0]);
    358 }
    359 
    360 void SkiaShader::store(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix,
    361         GLuint* textureUnit, ProgramDescription* description,
    362         SkiaShaderData* outData) {
    363     if (tryStoreGradient(caches, shader, modelViewMatrix,
    364             textureUnit, description, &outData->gradientData)) {
    365         outData->skiaShaderType = kGradient_SkiaShaderType;
    366         return;
    367     }
    368 
    369     if (tryStoreBitmap(caches, shader, modelViewMatrix,
    370             textureUnit, description, &outData->bitmapData)) {
    371         outData->skiaShaderType = kBitmap_SkiaShaderType;
    372         return;
    373     }
    374 
    375     if (tryStoreCompose(caches, shader, modelViewMatrix,
    376             textureUnit, description, outData)) {
    377         outData->skiaShaderType = kCompose_SkiaShaderType;
    378         return;
    379     }
    380 
    381     if (tryStoreLayer(caches, shader, modelViewMatrix,
    382             textureUnit, description, &outData->layerData)) {
    383         outData->skiaShaderType = kLayer_SkiaShaderType;
    384         return;
    385     }
    386 
    387     // Unknown/unsupported type, so explicitly ignore shader
    388     outData->skiaShaderType = kNone_SkiaShaderType;
    389 }
    390 
    391 void SkiaShader::apply(Caches& caches, const SkiaShaderData& data) {
    392     if (!data.skiaShaderType) return;
    393 
    394     if (data.skiaShaderType & kGradient_SkiaShaderType) {
    395         applyGradient(caches, data.gradientData);
    396     }
    397     if (data.skiaShaderType & kBitmap_SkiaShaderType) {
    398         applyBitmap(caches, data.bitmapData);
    399     }
    400 
    401     if (data.skiaShaderType == kLayer_SkiaShaderType) {
    402         applyLayer(caches, data.layerData);
    403     }
    404 }
    405 
    406 }; // namespace uirenderer
    407 }; // namespace android
    408