SkiaShader.cpp revision 3f085429fd47ebd32ac2463b3eae2a5a6c17be25
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 "SkiaShader.h"
25#include "Texture.h"
26#include "Matrix.h"
27
28namespace android {
29namespace uirenderer {
30
31///////////////////////////////////////////////////////////////////////////////
32// Support
33///////////////////////////////////////////////////////////////////////////////
34
35static const GLint 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/**
42 * This function does not work for n == 0.
43 */
44static inline bool isPowerOfTwo(unsigned int n) {
45    return !(n & (n - 1));
46}
47
48static inline void bindUniformColor(int slot, uint32_t color) {
49    const float a = ((color >> 24) & 0xff) / 255.0f;
50    glUniform4f(slot,
51            a * ((color >> 16) & 0xff) / 255.0f,
52            a * ((color >>  8) & 0xff) / 255.0f,
53            a * ((color      ) & 0xff) / 255.0f,
54            a);
55}
56
57///////////////////////////////////////////////////////////////////////////////
58// Base shader
59///////////////////////////////////////////////////////////////////////////////
60
61void SkiaShader::copyFrom(const SkiaShader& shader) {
62    mType = shader.mType;
63    mKey = shader.mKey;
64    mTileX = shader.mTileX;
65    mTileY = shader.mTileY;
66    mBlend = shader.mBlend;
67    mUnitMatrix = shader.mUnitMatrix;
68    mShaderMatrix = shader.mShaderMatrix;
69    mGenerationId = shader.mGenerationId;
70}
71
72SkiaShader::SkiaShader(): mCaches(NULL) {
73}
74
75SkiaShader::SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX,
76        SkShader::TileMode tileY, const SkMatrix* matrix, bool blend):
77        mType(type), mKey(key), mTileX(tileX), mTileY(tileY), mBlend(blend),
78        mCaches(NULL) {
79    setMatrix(matrix);
80    mGenerationId = 0;
81}
82
83SkiaShader::~SkiaShader() {
84}
85
86void SkiaShader::describe(ProgramDescription& description, const Extensions& extensions) {
87}
88
89void SkiaShader::setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
90        GLuint* textureUnit) {
91}
92
93void SkiaShader::bindTexture(Texture* texture, GLenum wrapS, GLenum wrapT) {
94    mCaches->bindTexture(texture->id);
95    texture->setWrapST(wrapS, wrapT);
96}
97
98void SkiaShader::computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelView) {
99    screenSpace.loadMultiply(mUnitMatrix, mShaderMatrix);
100    screenSpace.multiply(modelView);
101}
102
103///////////////////////////////////////////////////////////////////////////////
104// Layer shader
105///////////////////////////////////////////////////////////////////////////////
106
107SkiaLayerShader::SkiaLayerShader(Layer* layer, const SkMatrix* matrix):
108        SkiaShader(kBitmap, NULL, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
109                matrix, layer->isBlend()), mLayer(layer) {
110    updateLocalMatrix(matrix);
111}
112
113SkiaShader* SkiaLayerShader::copy() {
114    SkiaLayerShader* copy = new SkiaLayerShader();
115    copy->copyFrom(*this);
116    copy->mLayer = mLayer;
117    return copy;
118}
119
120void SkiaLayerShader::describe(ProgramDescription& description, const Extensions& extensions) {
121    description.hasBitmap = true;
122}
123
124void SkiaLayerShader::setupProgram(Program* program, const mat4& modelView,
125        const Snapshot& snapshot, GLuint* textureUnit) {
126    GLuint textureSlot = (*textureUnit)++;
127    Caches::getInstance().activeTexture(textureSlot);
128
129    const float width = mLayer->getWidth();
130    const float height = mLayer->getHeight();
131
132    mat4 textureTransform;
133    computeScreenSpaceMatrix(textureTransform, modelView);
134
135    // Uniforms
136    mLayer->bindTexture();
137    mLayer->setWrap(GL_CLAMP_TO_EDGE);
138    mLayer->setFilter(GL_LINEAR);
139
140    glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
141    glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
142            GL_FALSE, &textureTransform.data[0]);
143    glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height);
144}
145
146///////////////////////////////////////////////////////////////////////////////
147// Bitmap shader
148///////////////////////////////////////////////////////////////////////////////
149
150SkiaBitmapShader::SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX,
151        SkShader::TileMode tileY, SkMatrix* matrix, bool blend):
152        SkiaShader(kBitmap, key, tileX, tileY, matrix, blend), mBitmap(bitmap), mTexture(NULL) {
153    updateLocalMatrix(matrix);
154}
155
156SkiaShader* SkiaBitmapShader::copy() {
157    SkiaBitmapShader* copy = new SkiaBitmapShader();
158    copy->copyFrom(*this);
159    copy->mBitmap = mBitmap;
160    return copy;
161}
162
163void SkiaBitmapShader::describe(ProgramDescription& description, const Extensions& extensions) {
164    Texture* texture = mCaches->textureCache.get(mBitmap);
165    if (!texture) return;
166    mTexture = texture;
167
168    const float width = texture->width;
169    const float height = texture->height;
170
171    description.hasBitmap = true;
172    // The driver does not support non-power of two mirrored/repeated
173    // textures, so do it ourselves
174    if (!extensions.hasNPot() && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) &&
175            (mTileX != SkShader::kClamp_TileMode || mTileY != SkShader::kClamp_TileMode)) {
176        description.isBitmapNpot = true;
177        description.bitmapWrapS = gTileModes[mTileX];
178        description.bitmapWrapT = gTileModes[mTileY];
179        mWrapS = GL_CLAMP_TO_EDGE;
180        mWrapT = GL_CLAMP_TO_EDGE;
181    } else {
182        mWrapS = gTileModes[mTileX];
183        mWrapT = gTileModes[mTileY];
184    }
185}
186
187void SkiaBitmapShader::setupProgram(Program* program, const mat4& modelView,
188        const Snapshot&, GLuint* textureUnit) {
189    GLuint textureSlot = (*textureUnit)++;
190    Caches::getInstance().activeTexture(textureSlot);
191
192    Texture* texture = mTexture;
193    mTexture = NULL;
194    if (!texture) return;
195    const AutoTexture autoCleanup(texture);
196
197    const float width = texture->width;
198    const float height = texture->height;
199
200    mat4 textureTransform;
201    computeScreenSpaceMatrix(textureTransform, modelView);
202
203    // Uniforms
204    bindTexture(texture, mWrapS, mWrapT);
205    texture->setFilter(GL_LINEAR);
206
207    glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
208    glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
209            GL_FALSE, &textureTransform.data[0]);
210    glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height);
211}
212
213///////////////////////////////////////////////////////////////////////////////
214// Linear gradient shader
215///////////////////////////////////////////////////////////////////////////////
216
217static void toUnitMatrix(const SkPoint pts[2], SkMatrix* matrix) {
218    SkVector vec = pts[1] - pts[0];
219    const float mag = vec.length();
220    const float inv = mag ? 1.0f / mag : 0;
221
222    vec.scale(inv);
223    matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
224    matrix->postTranslate(-pts[0].fX, -pts[0].fY);
225    matrix->postScale(inv, inv);
226}
227
228SkiaLinearGradientShader::SkiaLinearGradientShader(float* bounds, uint32_t* colors,
229        float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
230        SkMatrix* matrix, bool blend):
231        SkiaShader(kLinearGradient, key, tileMode, tileMode, matrix, blend),
232        mBounds(bounds), mColors(colors), mPositions(positions), mCount(count) {
233    SkPoint points[2];
234    points[0].set(bounds[0], bounds[1]);
235    points[1].set(bounds[2], bounds[3]);
236
237    SkMatrix unitMatrix;
238    toUnitMatrix(points, &unitMatrix);
239    mUnitMatrix.load(unitMatrix);
240
241    updateLocalMatrix(matrix);
242
243    mIsSimple = count == 2 && tileMode == SkShader::kClamp_TileMode;
244}
245
246SkiaLinearGradientShader::~SkiaLinearGradientShader() {
247    delete[] mBounds;
248    delete[] mColors;
249    delete[] mPositions;
250}
251
252SkiaShader* SkiaLinearGradientShader::copy() {
253    SkiaLinearGradientShader* copy = new SkiaLinearGradientShader();
254    copy->copyFrom(*this);
255    copy->mBounds = new float[4];
256    memcpy(copy->mBounds, mBounds, sizeof(float) * 4);
257    copy->mColors = new uint32_t[mCount];
258    memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount);
259    copy->mPositions = new float[mCount];
260    memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
261    copy->mCount = mCount;
262    copy->mIsSimple = mIsSimple;
263    return copy;
264}
265
266void SkiaLinearGradientShader::describe(ProgramDescription& description,
267        const Extensions& extensions) {
268    description.hasGradient = true;
269    description.gradientType = ProgramDescription::kGradientLinear;
270    description.isSimpleGradient = mIsSimple;
271}
272
273void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelView,
274        const Snapshot&, GLuint* textureUnit) {
275    if (CC_UNLIKELY(!mIsSimple)) {
276        GLuint textureSlot = (*textureUnit)++;
277        Caches::getInstance().activeTexture(textureSlot);
278
279        Texture* texture = mCaches->gradientCache.get(mColors, mPositions, mCount);
280
281        // Uniforms
282        bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
283        glUniform1i(program->getUniform("gradientSampler"), textureSlot);
284    } else {
285        bindUniformColor(program->getUniform("startColor"), mColors[0]);
286        bindUniformColor(program->getUniform("endColor"), mColors[1]);
287    }
288
289    Caches::getInstance().dither.setupProgram(program, textureUnit);
290
291    mat4 screenSpace;
292    computeScreenSpaceMatrix(screenSpace, modelView);
293    glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
294}
295
296///////////////////////////////////////////////////////////////////////////////
297// Circular gradient shader
298///////////////////////////////////////////////////////////////////////////////
299
300static void toCircularUnitMatrix(const float x, const float y, const float radius,
301        SkMatrix* matrix) {
302    const float inv = 1.0f / radius;
303    matrix->setTranslate(-x, -y);
304    matrix->postScale(inv, inv);
305}
306
307SkiaCircularGradientShader::SkiaCircularGradientShader(float x, float y, float radius,
308        uint32_t* colors, float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
309        SkMatrix* matrix, bool blend):
310        SkiaSweepGradientShader(kCircularGradient, colors, positions, count, key,
311                tileMode, matrix, blend) {
312    SkMatrix unitMatrix;
313    toCircularUnitMatrix(x, y, radius, &unitMatrix);
314    mUnitMatrix.load(unitMatrix);
315
316    updateLocalMatrix(matrix);
317}
318
319SkiaShader* SkiaCircularGradientShader::copy() {
320    SkiaCircularGradientShader* copy = new SkiaCircularGradientShader();
321    copy->copyFrom(*this);
322    copy->mColors = new uint32_t[mCount];
323    memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount);
324    copy->mPositions = new float[mCount];
325    memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
326    copy->mCount = mCount;
327    copy->mIsSimple = mIsSimple;
328    return copy;
329}
330
331void SkiaCircularGradientShader::describe(ProgramDescription& description,
332        const Extensions& extensions) {
333    description.hasGradient = true;
334    description.gradientType = ProgramDescription::kGradientCircular;
335    description.isSimpleGradient = mIsSimple;
336}
337
338///////////////////////////////////////////////////////////////////////////////
339// Sweep gradient shader
340///////////////////////////////////////////////////////////////////////////////
341
342static void toSweepUnitMatrix(const float x, const float y, SkMatrix* matrix) {
343    matrix->setTranslate(-x, -y);
344}
345
346SkiaSweepGradientShader::SkiaSweepGradientShader(float x, float y, uint32_t* colors,
347        float* positions, int count, SkShader* key, SkMatrix* matrix, bool blend):
348        SkiaShader(kSweepGradient, key, SkShader::kClamp_TileMode,
349                SkShader::kClamp_TileMode, matrix, blend),
350        mColors(colors), mPositions(positions), mCount(count) {
351    SkMatrix unitMatrix;
352    toSweepUnitMatrix(x, y, &unitMatrix);
353    mUnitMatrix.load(unitMatrix);
354
355    updateLocalMatrix(matrix);
356
357    mIsSimple = count == 2;
358}
359
360SkiaSweepGradientShader::SkiaSweepGradientShader(Type type, uint32_t* colors,
361        float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
362        SkMatrix* matrix, bool blend):
363        SkiaShader(type, key, tileMode, tileMode, matrix, blend),
364        mColors(colors), mPositions(positions), mCount(count) {
365    // protected method, that doesn't setup mUnitMatrix - should be handled by subclass
366
367    mIsSimple = count == 2 && tileMode == SkShader::kClamp_TileMode;
368}
369
370SkiaSweepGradientShader::~SkiaSweepGradientShader() {
371    delete[] mColors;
372    delete[] mPositions;
373}
374
375SkiaShader* SkiaSweepGradientShader::copy() {
376    SkiaSweepGradientShader* copy = new SkiaSweepGradientShader();
377    copy->copyFrom(*this);
378    copy->mColors = new uint32_t[mCount];
379    memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount);
380    copy->mPositions = new float[mCount];
381    memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
382    copy->mCount = mCount;
383    copy->mIsSimple = mIsSimple;
384    return copy;
385}
386
387void SkiaSweepGradientShader::describe(ProgramDescription& description,
388        const Extensions& extensions) {
389    description.hasGradient = true;
390    description.gradientType = ProgramDescription::kGradientSweep;
391    description.isSimpleGradient = mIsSimple;
392}
393
394void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelView,
395        const Snapshot& snapshot, GLuint* textureUnit) {
396    if (CC_UNLIKELY(!mIsSimple)) {
397        GLuint textureSlot = (*textureUnit)++;
398        Caches::getInstance().activeTexture(textureSlot);
399
400        Texture* texture = mCaches->gradientCache.get(mColors, mPositions, mCount);
401
402        // Uniforms
403        bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
404        glUniform1i(program->getUniform("gradientSampler"), textureSlot);
405    } else {
406       bindUniformColor(program->getUniform("startColor"), mColors[0]);
407       bindUniformColor(program->getUniform("endColor"), mColors[1]);
408    }
409
410    mCaches->dither.setupProgram(program, textureUnit);
411
412    mat4 screenSpace;
413    computeScreenSpaceMatrix(screenSpace, modelView);
414    glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
415}
416
417///////////////////////////////////////////////////////////////////////////////
418// Compose shader
419///////////////////////////////////////////////////////////////////////////////
420
421SkiaComposeShader::SkiaComposeShader(SkiaShader* first, SkiaShader* second,
422        SkXfermode::Mode mode, SkShader* key):
423        SkiaShader(kCompose, key, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
424        NULL, first->blend() || second->blend()),
425        mFirst(first), mSecond(second), mMode(mode), mCleanup(false) {
426}
427
428SkiaComposeShader::~SkiaComposeShader() {
429    if (mCleanup) {
430        delete mFirst;
431        delete mSecond;
432    }
433}
434
435SkiaShader* SkiaComposeShader::copy() {
436    SkiaComposeShader* copy = new SkiaComposeShader();
437    copy->copyFrom(*this);
438    copy->mFirst = mFirst->copy();
439    copy->mSecond = mSecond->copy();
440    copy->mMode = mMode;
441    copy->cleanup();
442    return copy;
443}
444
445void SkiaComposeShader::describe(ProgramDescription& description, const Extensions& extensions) {
446    mFirst->describe(description, extensions);
447    mSecond->describe(description, extensions);
448    if (mFirst->type() == kBitmap) {
449        description.isBitmapFirst = true;
450    }
451    description.shadersMode = mMode;
452}
453
454void SkiaComposeShader::setupProgram(Program* program, const mat4& modelView,
455        const Snapshot& snapshot, GLuint* textureUnit) {
456    // Apply this compose shader's local transform and pass it down to
457    // the child shaders. They will in turn apply their local transform
458    // to this matrix.
459    mat4 transform;
460    computeScreenSpaceMatrix(transform, modelView);
461
462    mFirst->setupProgram(program, transform, snapshot, textureUnit);
463    mSecond->setupProgram(program, transform, snapshot, textureUnit);
464}
465
466}; // namespace uirenderer
467}; // namespace android
468