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