SkiaShader.cpp revision 8aa195d7081b889f3a7b1f426cbd8556377aae5e
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, 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// Bitmap shader
105///////////////////////////////////////////////////////////////////////////////
106
107SkiaBitmapShader::SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX,
108        SkShader::TileMode tileY, SkMatrix* matrix, bool blend):
109        SkiaShader(kBitmap, key, tileX, tileY, matrix, blend), mBitmap(bitmap), mTexture(NULL) {
110    updateLocalMatrix(matrix);
111}
112
113SkiaShader* SkiaBitmapShader::copy() {
114    SkiaBitmapShader* copy = new SkiaBitmapShader();
115    copy->copyFrom(*this);
116    copy->mBitmap = mBitmap;
117    return copy;
118}
119
120void SkiaBitmapShader::describe(ProgramDescription& description, const Extensions& extensions) {
121    Texture* texture = mCaches->textureCache.get(mBitmap);
122    if (!texture) return;
123    mTexture = texture;
124
125    const float width = texture->width;
126    const float height = texture->height;
127
128    description.hasBitmap = true;
129    // The driver does not support non-power of two mirrored/repeated
130    // textures, so do it ourselves
131    if (!extensions.hasNPot() && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) &&
132            (mTileX != SkShader::kClamp_TileMode || mTileY != SkShader::kClamp_TileMode)) {
133        description.isBitmapNpot = true;
134        description.bitmapWrapS = gTileModes[mTileX];
135        description.bitmapWrapT = gTileModes[mTileY];
136        mWrapS = GL_CLAMP_TO_EDGE;
137        mWrapT = GL_CLAMP_TO_EDGE;
138    } else {
139        mWrapS = gTileModes[mTileX];
140        mWrapT = gTileModes[mTileY];
141    }
142}
143
144void SkiaBitmapShader::setupProgram(Program* program, const mat4& modelView,
145        const Snapshot& snapshot, GLuint* textureUnit) {
146    GLuint textureSlot = (*textureUnit)++;
147    Caches::getInstance().activeTexture(textureSlot);
148
149    Texture* texture = mTexture;
150    mTexture = NULL;
151    if (!texture) return;
152    const AutoTexture autoCleanup(texture);
153
154    const float width = texture->width;
155    const float height = texture->height;
156
157    mat4 textureTransform;
158    computeScreenSpaceMatrix(textureTransform, modelView);
159
160    // Uniforms
161    bindTexture(texture, mWrapS, mWrapT);
162    texture->setFilter(GL_LINEAR);
163
164    glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
165    glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
166            GL_FALSE, &textureTransform.data[0]);
167    glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height);
168}
169
170///////////////////////////////////////////////////////////////////////////////
171// Linear gradient shader
172///////////////////////////////////////////////////////////////////////////////
173
174static void toUnitMatrix(const SkPoint pts[2], SkMatrix* matrix) {
175    SkVector vec = pts[1] - pts[0];
176    const float mag = vec.length();
177    const float inv = mag ? 1.0f / mag : 0;
178
179    vec.scale(inv);
180    matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
181    matrix->postTranslate(-pts[0].fX, -pts[0].fY);
182    matrix->postScale(inv, inv);
183}
184
185SkiaLinearGradientShader::SkiaLinearGradientShader(float* bounds, uint32_t* colors,
186        float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
187        SkMatrix* matrix, bool blend):
188        SkiaShader(kLinearGradient, key, tileMode, tileMode, matrix, blend),
189        mBounds(bounds), mColors(colors), mPositions(positions), mCount(count) {
190    SkPoint points[2];
191    points[0].set(bounds[0], bounds[1]);
192    points[1].set(bounds[2], bounds[3]);
193
194    SkMatrix unitMatrix;
195    toUnitMatrix(points, &unitMatrix);
196    mUnitMatrix.load(unitMatrix);
197
198    updateLocalMatrix(matrix);
199
200    mIsSimple = count == 2 && tileMode == SkShader::kClamp_TileMode;
201}
202
203SkiaLinearGradientShader::~SkiaLinearGradientShader() {
204    delete[] mBounds;
205    delete[] mColors;
206    delete[] mPositions;
207}
208
209SkiaShader* SkiaLinearGradientShader::copy() {
210    SkiaLinearGradientShader* copy = new SkiaLinearGradientShader();
211    copy->copyFrom(*this);
212    copy->mBounds = new float[4];
213    memcpy(copy->mBounds, mBounds, sizeof(float) * 4);
214    copy->mColors = new uint32_t[mCount];
215    memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount);
216    copy->mPositions = new float[mCount];
217    memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
218    copy->mCount = mCount;
219    copy->mIsSimple = mIsSimple;
220    return copy;
221}
222
223void SkiaLinearGradientShader::describe(ProgramDescription& description,
224        const Extensions& extensions) {
225    description.hasGradient = true;
226    description.gradientType = ProgramDescription::kGradientLinear;
227    description.isSimpleGradient = mIsSimple;
228}
229
230void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelView,
231        const Snapshot& snapshot, GLuint* textureUnit) {
232    if (CC_UNLIKELY(!mIsSimple)) {
233        GLuint textureSlot = (*textureUnit)++;
234        Caches::getInstance().activeTexture(textureSlot);
235
236        Texture* texture = mCaches->gradientCache.get(mColors, mPositions, mCount);
237
238        // Uniforms
239        bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
240        glUniform1i(program->getUniform("gradientSampler"), textureSlot);
241    } else {
242        bindUniformColor(program->getUniform("startColor"), mColors[0]);
243        bindUniformColor(program->getUniform("endColor"), mColors[1]);
244    }
245
246    Caches::getInstance().dither.setupProgram(program, textureUnit);
247
248    mat4 screenSpace;
249    computeScreenSpaceMatrix(screenSpace, modelView);
250    glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
251}
252
253///////////////////////////////////////////////////////////////////////////////
254// Circular gradient shader
255///////////////////////////////////////////////////////////////////////////////
256
257static void toCircularUnitMatrix(const float x, const float y, const float radius,
258        SkMatrix* matrix) {
259    const float inv = 1.0f / radius;
260    matrix->setTranslate(-x, -y);
261    matrix->postScale(inv, inv);
262}
263
264SkiaCircularGradientShader::SkiaCircularGradientShader(float x, float y, float radius,
265        uint32_t* colors, float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
266        SkMatrix* matrix, bool blend):
267        SkiaSweepGradientShader(kCircularGradient, x, y, colors, positions, count, key,
268                tileMode, matrix, blend) {
269    SkMatrix unitMatrix;
270    toCircularUnitMatrix(x, y, radius, &unitMatrix);
271    mUnitMatrix.load(unitMatrix);
272
273    updateLocalMatrix(matrix);
274}
275
276SkiaShader* SkiaCircularGradientShader::copy() {
277    SkiaCircularGradientShader* copy = new SkiaCircularGradientShader();
278    copy->copyFrom(*this);
279    copy->mColors = new uint32_t[mCount];
280    memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount);
281    copy->mPositions = new float[mCount];
282    memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
283    copy->mCount = mCount;
284    copy->mIsSimple = mIsSimple;
285    return copy;
286}
287
288void SkiaCircularGradientShader::describe(ProgramDescription& description,
289        const Extensions& extensions) {
290    description.hasGradient = true;
291    description.gradientType = ProgramDescription::kGradientCircular;
292    description.isSimpleGradient = mIsSimple;
293}
294
295///////////////////////////////////////////////////////////////////////////////
296// Sweep gradient shader
297///////////////////////////////////////////////////////////////////////////////
298
299static void toSweepUnitMatrix(const float x, const float y, SkMatrix* matrix) {
300    matrix->setTranslate(-x, -y);
301}
302
303SkiaSweepGradientShader::SkiaSweepGradientShader(float x, float y, uint32_t* colors,
304        float* positions, int count, SkShader* key, SkMatrix* matrix, bool blend):
305        SkiaShader(kSweepGradient, key, SkShader::kClamp_TileMode,
306                SkShader::kClamp_TileMode, matrix, blend),
307        mColors(colors), mPositions(positions), mCount(count) {
308    SkMatrix unitMatrix;
309    toSweepUnitMatrix(x, y, &unitMatrix);
310    mUnitMatrix.load(unitMatrix);
311
312    updateLocalMatrix(matrix);
313
314    mIsSimple = count == 2;
315}
316
317SkiaSweepGradientShader::SkiaSweepGradientShader(Type type, float x, float y, uint32_t* colors,
318        float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
319        SkMatrix* matrix, bool blend):
320        SkiaShader(type, key, tileMode, tileMode, matrix, blend),
321        mColors(colors), mPositions(positions), mCount(count) {
322
323    mIsSimple = count == 2 && tileMode == SkShader::kClamp_TileMode;
324}
325
326SkiaSweepGradientShader::~SkiaSweepGradientShader() {
327    delete[] mColors;
328    delete[] mPositions;
329}
330
331SkiaShader* SkiaSweepGradientShader::copy() {
332    SkiaSweepGradientShader* copy = new SkiaSweepGradientShader();
333    copy->copyFrom(*this);
334    copy->mColors = new uint32_t[mCount];
335    memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount);
336    copy->mPositions = new float[mCount];
337    memcpy(copy->mPositions, mPositions, sizeof(float) * mCount);
338    copy->mCount = mCount;
339    copy->mIsSimple = mIsSimple;
340    return copy;
341}
342
343void SkiaSweepGradientShader::describe(ProgramDescription& description,
344        const Extensions& extensions) {
345    description.hasGradient = true;
346    description.gradientType = ProgramDescription::kGradientSweep;
347    description.isSimpleGradient = mIsSimple;
348}
349
350void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelView,
351        const Snapshot& snapshot, GLuint* textureUnit) {
352    if (CC_UNLIKELY(!mIsSimple)) {
353        GLuint textureSlot = (*textureUnit)++;
354        Caches::getInstance().activeTexture(textureSlot);
355
356        Texture* texture = mCaches->gradientCache.get(mColors, mPositions, mCount);
357
358        // Uniforms
359        bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
360        glUniform1i(program->getUniform("gradientSampler"), textureSlot);
361    } else {
362       bindUniformColor(program->getUniform("startColor"), mColors[0]);
363       bindUniformColor(program->getUniform("endColor"), mColors[1]);
364    }
365
366    mCaches->dither.setupProgram(program, textureUnit);
367
368    mat4 screenSpace;
369    computeScreenSpaceMatrix(screenSpace, modelView);
370    glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
371}
372
373///////////////////////////////////////////////////////////////////////////////
374// Compose shader
375///////////////////////////////////////////////////////////////////////////////
376
377SkiaComposeShader::SkiaComposeShader(SkiaShader* first, SkiaShader* second,
378        SkXfermode::Mode mode, SkShader* key):
379        SkiaShader(kCompose, key, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
380        NULL, first->blend() || second->blend()),
381        mFirst(first), mSecond(second), mMode(mode), mCleanup(false) {
382}
383
384SkiaComposeShader::~SkiaComposeShader() {
385    if (mCleanup) {
386        delete mFirst;
387        delete mSecond;
388    }
389}
390
391SkiaShader* SkiaComposeShader::copy() {
392    SkiaComposeShader* copy = new SkiaComposeShader();
393    copy->copyFrom(*this);
394    copy->mFirst = mFirst->copy();
395    copy->mSecond = mSecond->copy();
396    copy->mMode = mMode;
397    copy->cleanup();
398    return copy;
399}
400
401void SkiaComposeShader::describe(ProgramDescription& description, const Extensions& extensions) {
402    mFirst->describe(description, extensions);
403    mSecond->describe(description, extensions);
404    if (mFirst->type() == kBitmap) {
405        description.isBitmapFirst = true;
406    }
407    description.shadersMode = mMode;
408}
409
410void SkiaComposeShader::setupProgram(Program* program, const mat4& modelView,
411        const Snapshot& snapshot, GLuint* textureUnit) {
412    // Apply this compose shader's local transform and pass it down to
413    // the child shaders. They will in turn apply their local transform
414    // to this matrix.
415    mat4 transform;
416    computeScreenSpaceMatrix(transform, modelView);
417
418    mFirst->setupProgram(program, transform, snapshot, textureUnit);
419    mSecond->setupProgram(program, transform, snapshot, textureUnit);
420}
421
422}; // namespace uirenderer
423}; // namespace android
424