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