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