blob: 63f9733b11f6b1e96a4f11cdc02a838b7cf7e213 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
reed@android.com8a1c16f2008-12-17 15:59:43 +00008#include "SkGL.h"
9#include "SkColorPriv.h"
10#include "SkGeometry.h"
11#include "SkPaint.h"
12#include "SkPath.h"
13#include "SkTemplates.h"
14#include "SkXfermode.h"
15
16//#define TRACE_TEXTURE_CREATION
17
18///////////////////////////////////////////////////////////////////////////////
19
20#ifdef SK_GL_HAS_COLOR4UB
21static inline void gl_pmcolor(U8CPU r, U8CPU g, U8CPU b, U8CPU a) {
22 glColor4ub(r, g, b, a);
23}
24
25void SkGL::SetAlpha(U8CPU alpha) {
26 glColor4ub(alpha, alpha, alpha, alpha);
27}
28#else
29static inline SkFixed byte2fixed(U8CPU value) {
30 return (value + (value >> 7)) << 8;
31}
32
33static inline void gl_pmcolor(U8CPU r, U8CPU g, U8CPU b, U8CPU a) {
34 glColor4x(byte2fixed(r), byte2fixed(g), byte2fixed(b), byte2fixed(a));
35}
36
37void SkGL::SetAlpha(U8CPU alpha) {
38 SkFixed fa = byte2fixed(alpha);
39 glColor4x(fa, fa, fa, fa);
40}
41#endif
42
43void SkGL::SetColor(SkColor c) {
44 SkPMColor pm = SkPreMultiplyColor(c);
45 gl_pmcolor(SkGetPackedR32(pm),
46 SkGetPackedG32(pm),
47 SkGetPackedB32(pm),
48 SkGetPackedA32(pm));
49}
50
51static const GLenum gXfermodeCoeff2Blend[] = {
52 GL_ZERO,
53 GL_ONE,
54 GL_SRC_COLOR,
55 GL_ONE_MINUS_SRC_COLOR,
56 GL_DST_COLOR,
57 GL_ONE_MINUS_DST_COLOR,
58 GL_SRC_ALPHA,
59 GL_ONE_MINUS_SRC_ALPHA,
60 GL_DST_ALPHA,
61 GL_ONE_MINUS_DST_ALPHA,
62};
63
64void SkGL::SetPaint(const SkPaint& paint, bool isPremul, bool justAlpha) {
65 if (justAlpha) {
66 SkGL::SetAlpha(paint.getAlpha());
67 } else {
68 SkGL::SetColor(paint.getColor());
69 }
70
71 GLenum sm = GL_ONE;
72 GLenum dm = GL_ONE_MINUS_SRC_ALPHA;
73
74 SkXfermode* mode = paint.getXfermode();
75 SkXfermode::Coeff sc, dc;
76 if (mode && mode->asCoeff(&sc, &dc)) {
77 sm = gXfermodeCoeff2Blend[sc];
78 dm = gXfermodeCoeff2Blend[dc];
79 }
80
81 // hack for text, which is not-premul (afaik)
82 if (!isPremul) {
83 if (GL_ONE == sm) {
84 sm = GL_SRC_ALPHA;
85 }
86 }
87
88 glEnable(GL_BLEND);
89 glBlendFunc(sm, dm);
90
91 if (paint.isDither()) {
92 glEnable(GL_DITHER);
93 } else {
94 glDisable(GL_DITHER);
95 }
96}
97
98///////////////////////////////////////////////////////////////////////////////
99
100void SkGL::DumpError(const char caller[]) {
101 GLenum err = glGetError();
102 if (err) {
103 SkDebugf("---- glGetError(%s) %d\n", caller, err);
104 }
105}
106
107void SkGL::SetRGBA(uint8_t rgba[], const SkColor src[], int count) {
108 for (int i = 0; i < count; i++) {
109 SkPMColor c = SkPreMultiplyColor(*src++);
110 *rgba++ = SkGetPackedR32(c);
111 *rgba++ = SkGetPackedG32(c);
112 *rgba++ = SkGetPackedB32(c);
113 *rgba++ = SkGetPackedA32(c);
114 }
115}
116
117///////////////////////////////////////////////////////////////////////////////
118
119void SkGL::Scissor(const SkIRect& r, int viewportHeight) {
120 glScissor(r.fLeft, viewportHeight - r.fBottom, r.width(), r.height());
121}
122
123///////////////////////////////////////////////////////////////////////////////
124
125void SkGL::Ortho(float left, float right, float bottom, float top,
126 float near, float far) {
127
128 float mat[16];
129
reed@android.com4516f472009-06-29 16:25:36 +0000130 sk_bzero(mat, sizeof(mat));
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131
132 mat[0] = 2 / (right - left);
133 mat[5] = 2 / (top - bottom);
134 mat[10] = 2 / (near - far);
135 mat[15] = 1;
136
137 mat[12] = (right + left) / (left - right);
138 mat[13] = (top + bottom) / (bottom - top);
139 mat[14] = (far + near) / (near - far);
140
141 glMultMatrixf(mat);
142}
143
144///////////////////////////////////////////////////////////////////////////////
145
146static bool canBeTexture(const SkBitmap& bm, GLenum* format, GLenum* type) {
147 switch (bm.config()) {
148 case SkBitmap::kARGB_8888_Config:
149 *format = GL_RGBA;
150 *type = GL_UNSIGNED_BYTE;
151 break;
152 case SkBitmap::kRGB_565_Config:
153 *format = GL_RGB;
154 *type = GL_UNSIGNED_SHORT_5_6_5;
155 break;
156 case SkBitmap::kARGB_4444_Config:
157 *format = GL_RGBA;
158 *type = GL_UNSIGNED_SHORT_4_4_4_4;
159 break;
160 case SkBitmap::kIndex8_Config:
161#ifdef SK_GL_SUPPORT_COMPRESSEDTEXIMAGE2D
162 *format = GL_PALETTE8_RGBA8_OES;
163 *type = GL_UNSIGNED_BYTE; // unused I think
164#else
165 // we promote index to argb32
166 *format = GL_RGBA;
167 *type = GL_UNSIGNED_BYTE;
168#endif
169 break;
170 case SkBitmap::kA8_Config:
171 *format = GL_ALPHA;
172 *type = GL_UNSIGNED_BYTE;
173 break;
174 default:
175 return false;
176 }
177 return true;
178}
179
180#define SK_GL_SIZE_OF_PALETTE (256 * sizeof(SkPMColor))
181
182size_t SkGL::ComputeTextureMemorySize(const SkBitmap& bitmap) {
183 int shift = 0;
184 size_t adder = 0;
185 switch (bitmap.config()) {
186 case SkBitmap::kARGB_8888_Config:
187 case SkBitmap::kRGB_565_Config:
188 case SkBitmap::kARGB_4444_Config:
189 case SkBitmap::kA8_Config:
190 // we're good as is
191 break;
192 case SkBitmap::kIndex8_Config:
193#ifdef SK_GL_SUPPORT_COMPRESSEDTEXIMAGE2D
194 // account for the colortable
195 adder = SK_GL_SIZE_OF_PALETTE;
196#else
197 // we promote index to argb32
198 shift = 2;
199#endif
200 break;
201 default:
202 return 0;
203 }
204 return (bitmap.getSize() << shift) + adder;
205}
206
207#ifdef SK_GL_SUPPORT_COMPRESSEDTEXIMAGE2D
208/* Fill out buffer with the compressed format GL expects from a colortable
209 based bitmap. [palette (colortable) + indices].
210
211 At the moment I always take the 8bit version, since that's what my data
212 is. I could detect that the colortable.count is <= 16, and then repack the
213 indices as nibbles to save RAM, but it would take more time (i.e. a lot
214 slower than memcpy), so I'm skipping that for now.
215
216 GL wants a full 256 palette entry, even though my ctable is only as big
217 as the colortable.count says it is. I presume it is OK to leave any
218 trailing entries uninitialized, since none of my indices should exceed
219 ctable->count().
220*/
221static void build_compressed_data(void* buffer, const SkBitmap& bitmap) {
222 SkASSERT(SkBitmap::kIndex8_Config == bitmap.config());
223
224 SkColorTable* ctable = bitmap.getColorTable();
225 uint8_t* dst = (uint8_t*)buffer;
226
227 memcpy(dst, ctable->lockColors(), ctable->count() * sizeof(SkPMColor));
228 ctable->unlockColors(false);
229
230 // always skip a full 256 number of entries, even if we memcpy'd fewer
231 dst += SK_GL_SIZE_OF_PALETTE;
wjmaclean@chromium.org0bde1802010-12-22 17:43:54 +0000232 memcpy(dst, bitmap.getPixels(), bitmap.getSafeSize()); // Just copy what we need.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233}
234#endif
235
236/* Return true if the bitmap cannot be supported in its current config as a
237 texture, and it needs to be promoted to ARGB32.
238 */
239static bool needToPromoteTo32bit(const SkBitmap& bitmap) {
240 if (bitmap.config() == SkBitmap::kIndex8_Config) {
241#ifdef SK_GL_SUPPORT_COMPRESSEDTEXIMAGE2D
242 const int w = bitmap.width();
243 const int h = bitmap.height();
244 if (SkNextPow2(w) == w && SkNextPow2(h) == h) {
245 // we can handle Indx8 if we're a POW2
246 return false;
247 }
248#endif
249 return true; // must promote to ARGB32
250 }
251 return false;
252}
253
254GLuint SkGL::BindNewTexture(const SkBitmap& origBitmap, SkPoint* max) {
255 SkBitmap tmpBitmap;
256 const SkBitmap* bitmap = &origBitmap;
257
258 if (needToPromoteTo32bit(origBitmap)) {
259 origBitmap.copyTo(&tmpBitmap, SkBitmap::kARGB_8888_Config);
260 // now bitmap points to our temp, which has been promoted to 32bits
261 bitmap = &tmpBitmap;
262 }
263
264 GLenum format, type;
265 if (!canBeTexture(*bitmap, &format, &type)) {
266 return 0;
267 }
268
269 SkAutoLockPixels alp(*bitmap);
270 if (!bitmap->readyToDraw()) {
271 return 0;
272 }
273
274 GLuint textureName;
275 glGenTextures(1, &textureName);
276
277 glBindTexture(GL_TEXTURE_2D, textureName);
278
279 // express rowbytes as a number of pixels for ow
280 int ow = bitmap->rowBytesAsPixels();
281 int oh = bitmap->height();
282 int nw = SkNextPow2(ow);
283 int nh = SkNextPow2(oh);
284
285 glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
286
287 // check if we need to scale to create power-of-2 dimensions
288#ifdef SK_GL_SUPPORT_COMPRESSEDTEXIMAGE2D
289 if (SkBitmap::kIndex8_Config == bitmap->config()) {
290 size_t imagesize = bitmap->getSize() + SK_GL_SIZE_OF_PALETTE;
291 SkAutoMalloc storage(imagesize);
292
293 build_compressed_data(storage.get(), *bitmap);
294 // we only support POW2 here (GLES 1.0 restriction)
295 SkASSERT(ow == nw);
296 SkASSERT(oh == nh);
297 glCompressedTexImage2D(GL_TEXTURE_2D, 0, format, ow, oh, 0,
298 imagesize, storage.get());
299 } else // fall through to non-compressed logic
300#endif
301 {
302 if (ow != nw || oh != nh) {
303 glTexImage2D(GL_TEXTURE_2D, 0, format, nw, nh, 0,
304 format, type, NULL);
305 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, ow, oh,
306 format, type, bitmap->getPixels());
307 } else {
308 // easy case, the bitmap is already pow2
309 glTexImage2D(GL_TEXTURE_2D, 0, format, ow, oh, 0,
310 format, type, bitmap->getPixels());
311 }
312 }
313
314#ifdef TRACE_TEXTURE_CREATION
315 SkDebugf("--- new texture [%d] size=(%d %d) bpp=%d\n", textureName, ow, oh,
316 bitmap->bytesPerPixel());
317#endif
318
319 if (max) {
320 max->fX = SkFixedToScalar(bitmap->width() << (16 - SkNextLog2(nw)));
321 max->fY = SkFixedToScalar(oh << (16 - SkNextLog2(nh)));
322 }
323 return textureName;
324}
325
326static const GLenum gTileMode2GLWrap[] = {
327 GL_CLAMP_TO_EDGE,
328 GL_REPEAT,
329#if GL_VERSION_ES_CM_1_0
330 GL_REPEAT // GLES doesn't support MIRROR
331#else
332 GL_MIRRORED_REPEAT
333#endif
334};
335
336void SkGL::SetTexParams(bool doFilter,
337 SkShader::TileMode tx, SkShader::TileMode ty) {
338 SkASSERT((unsigned)tx < SK_ARRAY_COUNT(gTileMode2GLWrap));
339 SkASSERT((unsigned)ty < SK_ARRAY_COUNT(gTileMode2GLWrap));
340
341 GLenum filter = doFilter ? GL_LINEAR : GL_NEAREST;
342
343 SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
344 SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
345 SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gTileMode2GLWrap[tx]);
346 SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gTileMode2GLWrap[ty]);
347}
348
349void SkGL::SetTexParamsClamp(bool doFilter) {
350 GLenum filter = doFilter ? GL_LINEAR : GL_NEAREST;
351
352 SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
353 SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
354 SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
355 SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
356}
357
358///////////////////////////////////////////////////////////////////////////////
359
360void SkGL::DrawVertices(int count, GLenum mode,
361 const SkGLVertex* SK_RESTRICT vertex,
362 const SkGLVertex* SK_RESTRICT texCoords,
363 const uint8_t* SK_RESTRICT colorArray,
364 const uint16_t* SK_RESTRICT indexArray,
365 SkGLClipIter* iter) {
366 SkASSERT(NULL != vertex);
367
368 if (NULL != texCoords) {
369 glEnable(GL_TEXTURE_2D);
370 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
371 glTexCoordPointer(2, SK_GLType, 0, texCoords);
372 } else {
373 glDisable(GL_TEXTURE_2D);
374 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
375 }
376
377 if (NULL != colorArray) {
378 glEnableClientState(GL_COLOR_ARRAY);
379 glColorPointer(4, GL_UNSIGNED_BYTE, 0, colorArray);
380 glShadeModel(GL_SMOOTH);
381 } else {
382 glDisableClientState(GL_COLOR_ARRAY);
383 glShadeModel(GL_FLAT);
384 }
385
386 glVertexPointer(2, SK_GLType, 0, vertex);
387
388 if (NULL != indexArray) {
389 if (iter) {
390 while (!iter->done()) {
391 iter->scissor();
392 glDrawElements(mode, count, GL_UNSIGNED_SHORT, indexArray);
393 iter->next();
394 }
395 } else {
396 glDrawElements(mode, count, GL_UNSIGNED_SHORT, indexArray);
397 }
398 } else {
399 if (iter) {
400 while (!iter->done()) {
401 iter->scissor();
402 glDrawArrays(mode, 0, count);
403 iter->next();
404 }
405 } else {
406 glDrawArrays(mode, 0, count);
407 }
408 }
409}
410
411void SkGL::PrepareForFillPath(SkPaint* paint) {
412 if (paint->getStrokeWidth() <= 0) {
413 paint->setStrokeWidth(SK_Scalar1);
414 }
415}
416
417void SkGL::FillPath(const SkPath& path, const SkPaint& paint, bool useTex,
418 SkGLClipIter* iter) {
419 SkPaint p(paint);
420 SkPath fillPath;
421
422 SkGL::PrepareForFillPath(&p);
423 p.getFillPath(path, &fillPath);
424 SkGL::DrawPath(fillPath, useTex, iter);
425}
426
427// should return max of all contours, rather than the sum (to save temp RAM)
428static int worst_case_edge_count(const SkPath& path) {
429 int edgeCount = 0;
430
431 SkPath::Iter iter(path, true);
432 SkPath::Verb verb;
433
434 while ((verb = iter.next(NULL)) != SkPath::kDone_Verb) {
435 switch (verb) {
436 case SkPath::kLine_Verb:
437 edgeCount += 1;
438 break;
439 case SkPath::kQuad_Verb:
440 edgeCount += 8;
441 break;
442 case SkPath::kCubic_Verb:
443 edgeCount += 16;
444 break;
445 default:
446 break;
447 }
448 }
449 return edgeCount;
450}
451
452void SkGL::DrawPath(const SkPath& path, bool useTex, SkGLClipIter* clipIter) {
reed@android.comd252db02009-04-01 18:31:44 +0000453 const SkRect& bounds = path.getBounds();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000454 if (bounds.isEmpty()) {
455 return;
456 }
457
458 int maxPts = worst_case_edge_count(path);
459 // add 1 for center of fan, and 1 for closing edge
460 SkAutoSTMalloc<32, SkGLVertex> storage(maxPts + 2);
461 SkGLVertex* base = storage.get();
462 SkGLVertex* vert = base;
463 SkGLVertex* texs = useTex ? base : NULL;
464
465 SkPath::Iter pathIter(path, true);
466 SkPoint pts[4];
467
468 bool needEnd = false;
469
470 for (;;) {
471 switch (pathIter.next(pts)) {
472 case SkPath::kMove_Verb:
473 if (needEnd) {
474 SkGL::DrawVertices(vert - base, GL_TRIANGLE_FAN,
475 base, texs, NULL, NULL, clipIter);
476 clipIter->safeRewind();
477 vert = base;
478 }
479 needEnd = true;
480 // center of the FAN
481 vert->setScalars(bounds.centerX(), bounds.centerY());
482 vert++;
483 // add first edge point
484 vert->setPoint(pts[0]);
485 vert++;
486 break;
487 case SkPath::kLine_Verb:
488 vert->setPoint(pts[1]);
489 vert++;
490 break;
491 case SkPath::kQuad_Verb: {
492 const int n = 8;
493 const SkScalar dt = SK_Scalar1 / n;
494 SkScalar t = dt;
495 for (int i = 1; i < n; i++) {
496 SkPoint loc;
497 SkEvalQuadAt(pts, t, &loc, NULL);
498 t += dt;
499 vert->setPoint(loc);
500 vert++;
501 }
502 vert->setPoint(pts[2]);
503 vert++;
504 break;
505 }
506 case SkPath::kCubic_Verb: {
507 const int n = 16;
508 const SkScalar dt = SK_Scalar1 / n;
509 SkScalar t = dt;
510 for (int i = 1; i < n; i++) {
511 SkPoint loc;
512 SkEvalCubicAt(pts, t, &loc, NULL, NULL);
513 t += dt;
514 vert->setPoint(loc);
515 vert++;
516 }
517 vert->setPoint(pts[3]);
518 vert++;
519 break;
520 }
521 case SkPath::kClose_Verb:
522 break;
523 case SkPath::kDone_Verb:
524 goto FINISHED;
525 }
526 }
527FINISHED:
528 if (needEnd) {
529 SkGL::DrawVertices(vert - base, GL_TRIANGLE_FAN, base, texs,
530 NULL, NULL, clipIter);
531 }
532}
533