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