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#define LOG_TAG "OpenGLRenderer"
18
19#include <utils/Log.h>
20
21#include <SkMatrix.h>
22
23#include "Caches.h"
24#include "Layer.h"
25#include "Matrix.h"
26#include "SkiaShader.h"
27#include "Texture.h"
28
29namespace android {
30namespace uirenderer {
31
32///////////////////////////////////////////////////////////////////////////////
33// Support
34///////////////////////////////////////////////////////////////////////////////
35
36static const GLint gTileModes[] = {
37        GL_CLAMP_TO_EDGE,   // == SkShader::kClamp_TileMode
38        GL_REPEAT,          // == SkShader::kRepeat_Mode
39        GL_MIRRORED_REPEAT  // == SkShader::kMirror_TileMode
40};
41
42/**
43 * This function does not work for n == 0.
44 */
45static inline bool isPowerOfTwo(unsigned int n) {
46    return !(n & (n - 1));
47}
48
49static inline void bindUniformColor(int slot, uint32_t color) {
50    const float a = ((color >> 24) & 0xff) / 255.0f;
51    glUniform4f(slot,
52            a * ((color >> 16) & 0xff) / 255.0f,
53            a * ((color >>  8) & 0xff) / 255.0f,
54            a * ((color      ) & 0xff) / 255.0f,
55            a);
56}
57
58static inline void bindTexture(Caches* caches, Texture* texture, GLenum wrapS, GLenum wrapT) {
59    caches->bindTexture(texture->id);
60    texture->setWrapST(wrapS, wrapT);
61}
62
63/**
64 * Compute the matrix to transform to screen space.
65 * @param screenSpace Output param for the computed matrix.
66 * @param unitMatrix The unit matrix for gradient shaders, as returned by SkShader::asAGradient,
67 *      or identity.
68 * @param localMatrix Local matrix, as returned by SkShader::getLocalMatrix().
69 * @param modelViewMatrix Model view matrix, as supplied by the OpenGLRenderer.
70 */
71static void computeScreenSpaceMatrix(mat4& screenSpace, const SkMatrix& unitMatrix,
72        const SkMatrix& localMatrix, const mat4& modelViewMatrix) {
73    mat4 shaderMatrix;
74    // uses implicit construction
75    shaderMatrix.loadInverse(localMatrix);
76    // again, uses implicit construction
77    screenSpace.loadMultiply(unitMatrix, shaderMatrix);
78    screenSpace.multiply(modelViewMatrix);
79}
80
81// Returns true if one is a bitmap and the other is a gradient
82static bool bitmapAndGradient(SkiaShaderType type1, SkiaShaderType type2) {
83    return (type1 == kBitmap_SkiaShaderType && type2 == kGradient_SkiaShaderType)
84            || (type2 == kBitmap_SkiaShaderType && type1 == kGradient_SkiaShaderType);
85}
86
87SkiaShaderType SkiaShader::getType(const SkShader& shader) {
88    // First check for a gradient shader.
89    switch (shader.asAGradient(NULL)) {
90        case SkShader::kNone_GradientType:
91            // Not a gradient shader. Fall through to check for other types.
92            break;
93        case SkShader::kLinear_GradientType:
94        case SkShader::kRadial_GradientType:
95        case SkShader::kSweep_GradientType:
96            return kGradient_SkiaShaderType;
97        default:
98            // This is a Skia gradient that has no SkiaShader equivalent. Return None to skip.
99            return kNone_SkiaShaderType;
100    }
101
102    // The shader is not a gradient. Check for a bitmap shader.
103    if (shader.asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType) {
104        return kBitmap_SkiaShaderType;
105    }
106
107    // Check for a ComposeShader.
108    SkShader::ComposeRec rec;
109    if (shader.asACompose(&rec)) {
110        const SkiaShaderType shaderAType = getType(*rec.fShaderA);
111        const SkiaShaderType shaderBType = getType(*rec.fShaderB);
112
113        // Compose is only supported if one is a bitmap and the other is a
114        // gradient. Otherwise, return None to skip.
115        if (!bitmapAndGradient(shaderAType, shaderBType)) {
116            return kNone_SkiaShaderType;
117        }
118        return kCompose_SkiaShaderType;
119    }
120
121    if (shader.asACustomShader(NULL)) {
122        return kLayer_SkiaShaderType;
123    }
124
125    return kNone_SkiaShaderType;
126}
127
128typedef void (*describeProc)(Caches* caches, ProgramDescription& description,
129        const Extensions& extensions, const SkShader& shader);
130
131describeProc gDescribeProc[] = {
132    InvalidSkiaShader::describe,
133    SkiaBitmapShader::describe,
134    SkiaGradientShader::describe,
135    SkiaComposeShader::describe,
136    SkiaLayerShader::describe,
137};
138
139typedef void (*setupProgramProc)(Caches* caches, const mat4& modelViewMatrix,
140        GLuint* textureUnit, const Extensions& extensions, const SkShader& shader);
141
142setupProgramProc gSetupProgramProc[] = {
143    InvalidSkiaShader::setupProgram,
144    SkiaBitmapShader::setupProgram,
145    SkiaGradientShader::setupProgram,
146    SkiaComposeShader::setupProgram,
147    SkiaLayerShader::setupProgram,
148};
149
150void SkiaShader::describe(Caches* caches, ProgramDescription& description,
151        const Extensions& extensions, const SkShader& shader) {
152    gDescribeProc[getType(shader)](caches, description, extensions, shader);
153}
154
155void SkiaShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
156        GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) {
157
158    gSetupProgramProc[getType(shader)](caches, modelViewMatrix, textureUnit, extensions, shader);
159}
160
161///////////////////////////////////////////////////////////////////////////////
162// Layer shader
163///////////////////////////////////////////////////////////////////////////////
164
165void SkiaLayerShader::describe(Caches*, ProgramDescription& description,
166        const Extensions&, const SkShader& shader) {
167    description.hasBitmap = true;
168}
169
170void SkiaLayerShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
171        GLuint* textureUnit, const Extensions&, const SkShader& shader) {
172    Layer* layer;
173    if (!shader.asACustomShader(reinterpret_cast<void**>(&layer))) {
174        LOG_ALWAYS_FATAL("SkiaLayerShader::setupProgram called on the wrong type of shader!");
175    }
176
177    GLuint textureSlot = (*textureUnit)++;
178    caches->activeTexture(textureSlot);
179
180    const float width = layer->getWidth();
181    const float height = layer->getHeight();
182
183    mat4 textureTransform;
184    computeScreenSpaceMatrix(textureTransform, SkMatrix::I(), shader.getLocalMatrix(),
185            modelViewMatrix);
186
187
188    // Uniforms
189    layer->bindTexture();
190    layer->setWrap(GL_CLAMP_TO_EDGE);
191    layer->setFilter(GL_LINEAR);
192
193    Program* program = caches->currentProgram;
194    glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
195    glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
196            GL_FALSE, &textureTransform.data[0]);
197    glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height);
198}
199
200///////////////////////////////////////////////////////////////////////////////
201// Bitmap shader
202///////////////////////////////////////////////////////////////////////////////
203
204struct BitmapShaderInfo {
205    float width;
206    float height;
207    GLenum wrapS;
208    GLenum wrapT;
209    Texture* texture;
210};
211
212static bool bitmapShaderHelper(Caches* caches, ProgramDescription* description,
213        BitmapShaderInfo* shaderInfo,
214        const Extensions& extensions,
215        const SkBitmap& bitmap, SkShader::TileMode tileModes[2]) {
216    Texture* texture = caches->textureCache.get(&bitmap);
217    if (!texture) return false;
218
219    const float width = texture->width;
220    const float height = texture->height;
221    GLenum wrapS, wrapT;
222
223    if (description) {
224        description->hasBitmap = true;
225    }
226    // The driver does not support non-power of two mirrored/repeated
227    // textures, so do it ourselves
228    if (!extensions.hasNPot() && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) &&
229            (tileModes[0] != SkShader::kClamp_TileMode ||
230             tileModes[1] != SkShader::kClamp_TileMode)) {
231        if (description) {
232            description->isBitmapNpot = true;
233            description->bitmapWrapS = gTileModes[tileModes[0]];
234            description->bitmapWrapT = gTileModes[tileModes[1]];
235        }
236        wrapS = GL_CLAMP_TO_EDGE;
237        wrapT = GL_CLAMP_TO_EDGE;
238    } else {
239        wrapS = gTileModes[tileModes[0]];
240        wrapT = gTileModes[tileModes[1]];
241    }
242
243    if (shaderInfo) {
244        shaderInfo->width = width;
245        shaderInfo->height = height;
246        shaderInfo->wrapS = wrapS;
247        shaderInfo->wrapT = wrapT;
248        shaderInfo->texture = texture;
249    }
250    return true;
251}
252
253void SkiaBitmapShader::describe(Caches* caches, ProgramDescription& description,
254        const Extensions& extensions, const SkShader& shader) {
255    SkBitmap bitmap;
256    SkShader::TileMode xy[2];
257    if (shader.asABitmap(&bitmap, NULL, xy) != SkShader::kDefault_BitmapType) {
258        LOG_ALWAYS_FATAL("SkiaBitmapShader::describe called with a different kind of shader!");
259    }
260    bitmapShaderHelper(caches, &description, NULL, extensions, bitmap, xy);
261}
262
263void SkiaBitmapShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
264        GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) {
265    SkBitmap bitmap;
266    SkShader::TileMode xy[2];
267    if (shader.asABitmap(&bitmap, NULL, xy) != SkShader::kDefault_BitmapType) {
268        LOG_ALWAYS_FATAL("SkiaBitmapShader::setupProgram called with a different kind of shader!");
269    }
270
271    GLuint textureSlot = (*textureUnit)++;
272    Caches::getInstance().activeTexture(textureSlot);
273
274    BitmapShaderInfo shaderInfo;
275    if (!bitmapShaderHelper(caches, NULL, &shaderInfo, extensions, bitmap, xy)) {
276        return;
277    }
278
279    Program* program = caches->currentProgram;
280    Texture* texture = shaderInfo.texture;
281
282    const AutoTexture autoCleanup(texture);
283
284    mat4 textureTransform;
285    computeScreenSpaceMatrix(textureTransform, SkMatrix::I(), shader.getLocalMatrix(),
286            modelViewMatrix);
287
288    // Uniforms
289    bindTexture(caches, texture, shaderInfo.wrapS, shaderInfo.wrapT);
290    texture->setFilter(GL_LINEAR);
291
292    glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
293    glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
294            GL_FALSE, &textureTransform.data[0]);
295    glUniform2f(program->getUniform("textureDimension"), 1.0f / shaderInfo.width,
296            1.0f / shaderInfo.height);
297}
298
299///////////////////////////////////////////////////////////////////////////////
300// Linear gradient shader
301///////////////////////////////////////////////////////////////////////////////
302
303static void toUnitMatrix(const SkPoint pts[2], SkMatrix* matrix) {
304    SkVector vec = pts[1] - pts[0];
305    const float mag = vec.length();
306    const float inv = mag ? 1.0f / mag : 0;
307
308    vec.scale(inv);
309    matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
310    matrix->postTranslate(-pts[0].fX, -pts[0].fY);
311    matrix->postScale(inv, inv);
312}
313
314///////////////////////////////////////////////////////////////////////////////
315// Circular gradient shader
316///////////////////////////////////////////////////////////////////////////////
317
318static void toCircularUnitMatrix(const float x, const float y, const float radius,
319        SkMatrix* matrix) {
320    const float inv = 1.0f / radius;
321    matrix->setTranslate(-x, -y);
322    matrix->postScale(inv, inv);
323}
324
325///////////////////////////////////////////////////////////////////////////////
326// Sweep gradient shader
327///////////////////////////////////////////////////////////////////////////////
328
329static void toSweepUnitMatrix(const float x, const float y, SkMatrix* matrix) {
330    matrix->setTranslate(-x, -y);
331}
332
333///////////////////////////////////////////////////////////////////////////////
334// Common gradient code
335///////////////////////////////////////////////////////////////////////////////
336
337static bool isSimpleGradient(const SkShader::GradientInfo& gradInfo) {
338    return gradInfo.fColorCount == 2 && gradInfo.fTileMode == SkShader::kClamp_TileMode;
339}
340
341void SkiaGradientShader::describe(Caches*, ProgramDescription& description,
342        const Extensions& extensions, const SkShader& shader) {
343    SkShader::GradientInfo gradInfo;
344    gradInfo.fColorCount = 0;
345    gradInfo.fColors = NULL;
346    gradInfo.fColorOffsets = NULL;
347
348    switch (shader.asAGradient(&gradInfo)) {
349        case SkShader::kLinear_GradientType:
350            description.gradientType = ProgramDescription::kGradientLinear;
351            break;
352        case SkShader::kRadial_GradientType:
353            description.gradientType = ProgramDescription::kGradientCircular;
354            break;
355        case SkShader::kSweep_GradientType:
356            description.gradientType = ProgramDescription::kGradientSweep;
357            break;
358        default:
359            // Do nothing. This shader is unsupported.
360            return;
361    }
362    description.hasGradient = true;
363    description.isSimpleGradient = isSimpleGradient(gradInfo);
364}
365
366void SkiaGradientShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
367        GLuint* textureUnit, const Extensions&, const SkShader& shader) {
368    // SkShader::GradientInfo.fColorCount is an in/out parameter. As input, it tells asAGradient
369    // how much space has been allocated for fColors and fColorOffsets.  10 was chosen
370    // arbitrarily, but should be >= 2.
371    // As output, it tells the number of actual colors/offsets in the gradient.
372    const int COLOR_COUNT = 10;
373    SkAutoSTMalloc<COLOR_COUNT, SkColor> colorStorage(COLOR_COUNT);
374    SkAutoSTMalloc<COLOR_COUNT, SkScalar> positionStorage(COLOR_COUNT);
375
376    SkShader::GradientInfo gradInfo;
377    gradInfo.fColorCount = COLOR_COUNT;
378    gradInfo.fColors = colorStorage.get();
379    gradInfo.fColorOffsets = positionStorage.get();
380
381    SkShader::GradientType gradType = shader.asAGradient(&gradInfo);
382
383    Program* program = caches->currentProgram;
384    if (CC_UNLIKELY(!isSimpleGradient(gradInfo))) {
385        if (gradInfo.fColorCount > COLOR_COUNT) {
386            // There was not enough room in our arrays for all the colors and offsets. Try again,
387            // now that we know the true number of colors.
388            gradInfo.fColors = colorStorage.reset(gradInfo.fColorCount);
389            gradInfo.fColorOffsets = positionStorage.reset(gradInfo.fColorCount);
390
391            shader.asAGradient(&gradInfo);
392        }
393        GLuint textureSlot = (*textureUnit)++;
394        caches->activeTexture(textureSlot);
395
396#ifndef SK_SCALAR_IS_FLOAT
397    #error Need to convert gradInfo.fColorOffsets to float!
398#endif
399        Texture* texture = caches->gradientCache.get(gradInfo.fColors, gradInfo.fColorOffsets,
400                gradInfo.fColorCount);
401
402        // Uniforms
403        bindTexture(caches, texture, gTileModes[gradInfo.fTileMode], gTileModes[gradInfo.fTileMode]);
404        glUniform1i(program->getUniform("gradientSampler"), textureSlot);
405    } else {
406        bindUniformColor(program->getUniform("startColor"), gradInfo.fColors[0]);
407        bindUniformColor(program->getUniform("endColor"), gradInfo.fColors[1]);
408    }
409
410    caches->dither.setupProgram(program, textureUnit);
411
412    SkMatrix unitMatrix;
413    switch (gradType) {
414        case SkShader::kLinear_GradientType:
415            toUnitMatrix(gradInfo.fPoint, &unitMatrix);
416            break;
417        case SkShader::kRadial_GradientType:
418            toCircularUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY,
419                    gradInfo.fRadius[0], &unitMatrix);
420            break;
421        case SkShader::kSweep_GradientType:
422            toSweepUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, &unitMatrix);
423            break;
424        default:
425            LOG_ALWAYS_FATAL("Invalid SkShader gradient type %d", gradType);
426    }
427
428    mat4 screenSpace;
429    computeScreenSpaceMatrix(screenSpace, unitMatrix, shader.getLocalMatrix(), modelViewMatrix);
430    glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
431}
432
433///////////////////////////////////////////////////////////////////////////////
434// Compose shader
435///////////////////////////////////////////////////////////////////////////////
436
437void SkiaComposeShader::describe(Caches* caches, ProgramDescription& description,
438        const Extensions& extensions, const SkShader& shader) {
439    SkShader::ComposeRec rec;
440    if (!shader.asACompose(&rec)) {
441        LOG_ALWAYS_FATAL("SkiaComposeShader::describe called on the wrong shader type!");
442    }
443    SkiaShader::describe(caches, description, extensions, *rec.fShaderA);
444    SkiaShader::describe(caches, description, extensions, *rec.fShaderB);
445    if (SkiaShader::getType(*rec.fShaderA) == kBitmap_SkiaShaderType) {
446        description.isBitmapFirst = true;
447    }
448    if (!SkXfermode::AsMode(rec.fMode, &description.shadersMode)) {
449        // TODO: Support other modes.
450        description.shadersMode = SkXfermode::kSrcOver_Mode;
451    }
452}
453
454void SkiaComposeShader::setupProgram(Caches* caches, const mat4& modelViewMatrix,
455        GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) {
456    SkShader::ComposeRec rec;
457    if (!shader.asACompose(&rec)) {
458        LOG_ALWAYS_FATAL("SkiaComposeShader::setupProgram called on the wrong shader type!");
459    }
460
461    // Apply this compose shader's local transform and pass it down to
462    // the child shaders. They will in turn apply their local transform
463    // to this matrix.
464    mat4 transform;
465    computeScreenSpaceMatrix(transform, SkMatrix::I(), shader.getLocalMatrix(),
466            modelViewMatrix);
467
468    SkiaShader::setupProgram(caches, transform, textureUnit, extensions, *rec.fShaderA);
469    SkiaShader::setupProgram(caches, transform, textureUnit, extensions, *rec.fShaderB);
470}
471
472}; // namespace uirenderer
473}; // namespace android
474