SkiaShader.cpp revision 8164c2d338781c3a3c4a443941070dca5d88f2a7
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 "SkiaShader.h"
24#include "Texture.h"
25#include "Matrix.h"
26
27namespace android {
28namespace uirenderer {
29
30///////////////////////////////////////////////////////////////////////////////
31// Support
32///////////////////////////////////////////////////////////////////////////////
33
34static const GLenum gTextureUnitsMap[] = {
35        GL_TEXTURE0,
36        GL_TEXTURE1,
37        GL_TEXTURE2
38};
39
40static const GLint gTileModes[] = {
41        GL_CLAMP_TO_EDGE,   // == SkShader::kClamp_TileMode
42        GL_REPEAT,          // == SkShader::kRepeat_Mode
43        GL_MIRRORED_REPEAT  // == SkShader::kMirror_TileMode
44};
45
46///////////////////////////////////////////////////////////////////////////////
47// Base shader
48///////////////////////////////////////////////////////////////////////////////
49
50SkiaShader::SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX,
51        SkShader::TileMode tileY, SkMatrix* matrix, bool blend):
52        mType(type), mKey(key), mTileX(tileX), mTileY(tileY), mBlend(blend) {
53    setMatrix(matrix);
54}
55
56SkiaShader::~SkiaShader() {
57}
58
59void SkiaShader::describe(ProgramDescription& description, const Extensions& extensions) {
60}
61
62void SkiaShader::setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
63        GLuint* textureUnit) {
64}
65
66void SkiaShader::bindTexture(Texture* texture, GLenum wrapS, GLenum wrapT, GLuint textureUnit) {
67    glActiveTexture(gTextureUnitsMap[textureUnit]);
68    glBindTexture(GL_TEXTURE_2D, texture->id);
69    if (wrapS != texture->wrapS) {
70        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS);
71        texture->wrapS = wrapS;
72    }
73    if (wrapT != texture->wrapT) {
74        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT);
75        texture->wrapT = wrapT;
76    }
77}
78
79void SkiaShader::computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelView) {
80    screenSpace.loadMultiply(mUnitMatrix, mShaderMatrix);
81    screenSpace.multiply(modelView);
82}
83
84///////////////////////////////////////////////////////////////////////////////
85// Bitmap shader
86///////////////////////////////////////////////////////////////////////////////
87
88SkiaBitmapShader::SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX,
89        SkShader::TileMode tileY, SkMatrix* matrix, bool blend):
90        SkiaShader(kBitmap, key, tileX, tileY, matrix, blend), mBitmap(bitmap), mTexture(NULL) {
91    updateLocalMatrix(matrix);
92}
93
94void SkiaBitmapShader::describe(ProgramDescription& description, const Extensions& extensions) {
95    Texture* texture = mTextureCache->get(mBitmap);
96    if (!texture) return;
97    mTexture = texture;
98
99    const float width = texture->width;
100    const float height = texture->height;
101
102    description.hasBitmap = true;
103    // The driver does not support non-power of two mirrored/repeated
104    // textures, so do it ourselves
105    if (!extensions.hasNPot() && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) &&
106            (mTileX != SkShader::kClamp_TileMode || mTileY != SkShader::kClamp_TileMode)) {
107        description.isBitmapNpot = true;
108        description.bitmapWrapS = gTileModes[mTileX];
109        description.bitmapWrapT = gTileModes[mTileY];
110        mWrapS = GL_CLAMP_TO_EDGE;
111        mWrapT = GL_CLAMP_TO_EDGE;
112    } else {
113        mWrapS = gTileModes[mTileX];
114        mWrapT = gTileModes[mTileY];
115    }
116}
117
118void SkiaBitmapShader::setupProgram(Program* program, const mat4& modelView,
119        const Snapshot& snapshot, GLuint* textureUnit) {
120    GLuint textureSlot = (*textureUnit)++;
121    glActiveTexture(gTextureUnitsMap[textureSlot]);
122
123    Texture* texture = mTexture;
124    mTexture = NULL;
125    if (!texture) return;
126    const AutoTexture autoCleanup(texture);
127
128    const float width = texture->width;
129    const float height = texture->height;
130
131    mat4 textureTransform;
132    computeScreenSpaceMatrix(textureTransform, modelView);
133
134    // Uniforms
135    bindTexture(texture, mWrapS, mWrapT, textureSlot);
136    glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
137    glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
138            GL_FALSE, &textureTransform.data[0]);
139    glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height);
140}
141
142void SkiaBitmapShader::updateTransforms(Program* program, const mat4& modelView,
143        const Snapshot& snapshot) {
144    mat4 textureTransform;
145    computeScreenSpaceMatrix(textureTransform, modelView);
146    glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
147            GL_FALSE, &textureTransform.data[0]);
148}
149
150///////////////////////////////////////////////////////////////////////////////
151// Linear gradient shader
152///////////////////////////////////////////////////////////////////////////////
153
154static void toUnitMatrix(const SkPoint pts[2], SkMatrix* matrix) {
155    SkVector vec = pts[1] - pts[0];
156    const float mag = vec.length();
157    const float inv = mag ? 1.0f / mag : 0;
158
159    vec.scale(inv);
160    matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
161    matrix->postTranslate(-pts[0].fX, -pts[0].fY);
162    matrix->postScale(inv, inv);
163}
164
165SkiaLinearGradientShader::SkiaLinearGradientShader(float* bounds, uint32_t* colors,
166        float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
167        SkMatrix* matrix, bool blend):
168        SkiaShader(kLinearGradient, key, tileMode, tileMode, matrix, blend),
169        mBounds(bounds), mColors(colors), mPositions(positions), mCount(count) {
170    SkPoint points[2];
171    points[0].set(bounds[0], bounds[1]);
172    points[1].set(bounds[2], bounds[3]);
173
174    SkMatrix unitMatrix;
175    toUnitMatrix(points, &unitMatrix);
176    mUnitMatrix.load(unitMatrix);
177
178    updateLocalMatrix(matrix);
179}
180
181SkiaLinearGradientShader::~SkiaLinearGradientShader() {
182    delete[] mBounds;
183    delete[] mColors;
184    delete[] mPositions;
185}
186
187void SkiaLinearGradientShader::describe(ProgramDescription& description,
188        const Extensions& extensions) {
189    description.hasGradient = true;
190    description.gradientType = ProgramDescription::kGradientLinear;
191}
192
193void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelView,
194        const Snapshot& snapshot, GLuint* textureUnit) {
195    GLuint textureSlot = (*textureUnit)++;
196    glActiveTexture(gTextureUnitsMap[textureSlot]);
197
198    Texture* texture = mGradientCache->get(mKey);
199    if (!texture) {
200        texture = mGradientCache->addLinearGradient(mKey, mColors, mPositions, mCount, mTileX);
201    }
202
203    mat4 screenSpace;
204    computeScreenSpaceMatrix(screenSpace, modelView);
205
206    // Uniforms
207    bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY], textureSlot);
208    glUniform1i(program->getUniform("gradientSampler"), textureSlot);
209    glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
210}
211
212void SkiaLinearGradientShader::updateTransforms(Program* program, const mat4& modelView,
213        const Snapshot& snapshot) {
214    mat4 screenSpace;
215    computeScreenSpaceMatrix(screenSpace, modelView);
216    glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
217}
218
219///////////////////////////////////////////////////////////////////////////////
220// Circular gradient shader
221///////////////////////////////////////////////////////////////////////////////
222
223static void toCircularUnitMatrix(const float x, const float y, const float radius,
224        SkMatrix* matrix) {
225    const float inv = 1.0f / radius;
226    matrix->setTranslate(-x, -y);
227    matrix->postScale(inv, inv);
228}
229
230SkiaCircularGradientShader::SkiaCircularGradientShader(float x, float y, float radius,
231        uint32_t* colors, float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
232        SkMatrix* matrix, bool blend):
233        SkiaSweepGradientShader(kCircularGradient, x, y, colors, positions, count, key,
234                tileMode, matrix, blend) {
235    SkMatrix unitMatrix;
236    toCircularUnitMatrix(x, y, radius, &unitMatrix);
237    mUnitMatrix.load(unitMatrix);
238
239    updateLocalMatrix(matrix);
240}
241
242void SkiaCircularGradientShader::describe(ProgramDescription& description,
243        const Extensions& extensions) {
244    description.hasGradient = true;
245    description.gradientType = ProgramDescription::kGradientCircular;
246}
247
248///////////////////////////////////////////////////////////////////////////////
249// Sweep gradient shader
250///////////////////////////////////////////////////////////////////////////////
251
252static void toSweepUnitMatrix(const float x, const float y, SkMatrix* matrix) {
253    matrix->setTranslate(-x, -y);
254}
255
256SkiaSweepGradientShader::SkiaSweepGradientShader(float x, float y, uint32_t* colors,
257        float* positions, int count, SkShader* key, SkMatrix* matrix, bool blend):
258        SkiaShader(kSweepGradient, key, SkShader::kClamp_TileMode,
259                SkShader::kClamp_TileMode, matrix, blend),
260        mColors(colors), mPositions(positions), mCount(count) {
261    SkMatrix unitMatrix;
262    toSweepUnitMatrix(x, y, &unitMatrix);
263    mUnitMatrix.load(unitMatrix);
264
265    updateLocalMatrix(matrix);
266}
267
268SkiaSweepGradientShader::SkiaSweepGradientShader(Type type, float x, float y, uint32_t* colors,
269        float* positions, int count, SkShader* key, SkShader::TileMode tileMode,
270        SkMatrix* matrix, bool blend):
271        SkiaShader(type, key, tileMode, tileMode, matrix, blend),
272        mColors(colors), mPositions(positions), mCount(count) {
273}
274
275SkiaSweepGradientShader::~SkiaSweepGradientShader() {
276    delete[] mColors;
277    delete[] mPositions;
278}
279
280void SkiaSweepGradientShader::describe(ProgramDescription& description,
281        const Extensions& extensions) {
282    description.hasGradient = true;
283    description.gradientType = ProgramDescription::kGradientSweep;
284}
285
286void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelView,
287        const Snapshot& snapshot, GLuint* textureUnit) {
288    GLuint textureSlot = (*textureUnit)++;
289    glActiveTexture(gTextureUnitsMap[textureSlot]);
290
291    Texture* texture = mGradientCache->get(mKey);
292    if (!texture) {
293        texture = mGradientCache->addLinearGradient(mKey, mColors, mPositions, mCount);
294    }
295
296    mat4 screenSpace;
297    computeScreenSpaceMatrix(screenSpace, modelView);
298
299    // Uniforms
300    bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY], textureSlot);
301    glUniform1i(program->getUniform("gradientSampler"), textureSlot);
302    glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
303}
304
305void SkiaSweepGradientShader::updateTransforms(Program* program, const mat4& modelView,
306        const Snapshot& snapshot) {
307    mat4 screenSpace;
308    computeScreenSpaceMatrix(screenSpace, modelView);
309    glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]);
310}
311
312///////////////////////////////////////////////////////////////////////////////
313// Compose shader
314///////////////////////////////////////////////////////////////////////////////
315
316SkiaComposeShader::SkiaComposeShader(SkiaShader* first, SkiaShader* second,
317        SkXfermode::Mode mode, SkShader* key):
318        SkiaShader(kCompose, key, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
319        NULL, first->blend() || second->blend()), mFirst(first), mSecond(second), mMode(mode) {
320}
321
322void SkiaComposeShader::set(TextureCache* textureCache, GradientCache* gradientCache) {
323    SkiaShader::set(textureCache, gradientCache);
324    mFirst->set(textureCache, gradientCache);
325    mSecond->set(textureCache, gradientCache);
326}
327
328void SkiaComposeShader::describe(ProgramDescription& description, const Extensions& extensions) {
329    mFirst->describe(description, extensions);
330    mSecond->describe(description, extensions);
331    if (mFirst->type() == kBitmap) {
332        description.isBitmapFirst = true;
333    }
334    description.shadersMode = mMode;
335}
336
337void SkiaComposeShader::setupProgram(Program* program, const mat4& modelView,
338        const Snapshot& snapshot, GLuint* textureUnit) {
339    mFirst->setupProgram(program, modelView, snapshot, textureUnit);
340    mSecond->setupProgram(program, modelView, snapshot, textureUnit);
341}
342
343}; // namespace uirenderer
344}; // namespace android
345