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