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
14static inline void gl_pmcolor(U8CPU r, U8CPU g, U8CPU b, U8CPU a) {
15    glColor4ub(r, g, b, a);
16}
17
18void SkGL::SetAlpha(U8CPU alpha) {
19    glColor4ub(alpha, alpha, alpha, alpha);
20}
21#else
22static inline SkFixed byte2fixed(U8CPU value) {
23    return (value + (value >> 7)) << 8;
24}
25
26static inline void gl_pmcolor(U8CPU r, U8CPU g, U8CPU b, U8CPU a) {
27    glColor4x(byte2fixed(r), byte2fixed(g), byte2fixed(b), byte2fixed(a));
28}
29
30void SkGL::SetAlpha(U8CPU alpha) {
31    SkFixed fa = byte2fixed(alpha);
32    glColor4x(fa, fa, fa, fa);
33}
34#endif
35
36void 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
44static 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
57void 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
93void SkGL::DumpError(const char caller[]) {
94    GLenum err = glGetError();
95    if (err) {
96        SkDebugf("---- glGetError(%s) %d\n", caller, err);
97    }
98}
99
100void 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
112void SkGL::Scissor(const SkIRect& r, int viewportHeight) {
113    glScissor(r.fLeft, viewportHeight - r.fBottom, r.width(), r.height());
114}
115
116///////////////////////////////////////////////////////////////////////////////
117
118void SkGL::Ortho(float left, float right, float bottom, float top,
119                 float near, float far) {
120
121    float mat[16];
122
123    sk_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
139static 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
175size_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*/
214static 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 */
232static 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
247GLuint 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
319static 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
329void 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
342void 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
353void 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
404void SkGL::PrepareForFillPath(SkPaint* paint) {
405    if (paint->getStrokeWidth() <= 0) {
406        paint->setStrokeWidth(SK_Scalar1);
407    }
408}
409
410void 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)
421static 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
445void SkGL::DrawPath(const SkPath& path, bool useTex, SkGLClipIter* clipIter) {
446    const SkRect& bounds = path.getBounds();
447    if (bounds.isEmpty()) {
448        return;
449    }
450
451    int maxPts = worst_case_edge_count(path);
452    // add 1 for center of fan, and 1 for closing edge
453    SkAutoSTMalloc<32, SkGLVertex>  storage(maxPts + 2);
454    SkGLVertex* base = storage.get();
455    SkGLVertex* vert = base;
456    SkGLVertex* texs = useTex ? base : NULL;
457
458    SkPath::Iter    pathIter(path, true);
459    SkPoint         pts[4];
460
461    bool needEnd = false;
462
463    for (;;) {
464        switch (pathIter.next(pts)) {
465            case SkPath::kMove_Verb:
466                if (needEnd) {
467                    SkGL::DrawVertices(vert - base, GL_TRIANGLE_FAN,
468                                       base, texs, NULL, NULL, clipIter);
469                    clipIter->safeRewind();
470                    vert = base;
471                }
472                needEnd = true;
473                // center of the FAN
474                vert->setScalars(bounds.centerX(), bounds.centerY());
475                vert++;
476                // add first edge point
477                vert->setPoint(pts[0]);
478                vert++;
479                break;
480                case SkPath::kLine_Verb:
481                vert->setPoint(pts[1]);
482                vert++;
483                break;
484                case SkPath::kQuad_Verb: {
485                    const int n = 8;
486                    const SkScalar dt = SK_Scalar1 / n;
487                    SkScalar t = dt;
488                    for (int i = 1; i < n; i++) {
489                        SkPoint loc;
490                        SkEvalQuadAt(pts, t, &loc, NULL);
491                        t += dt;
492                        vert->setPoint(loc);
493                        vert++;
494                    }
495                    vert->setPoint(pts[2]);
496                    vert++;
497                    break;
498                }
499                case SkPath::kCubic_Verb: {
500                    const int n = 16;
501                    const SkScalar dt = SK_Scalar1 / n;
502                    SkScalar t = dt;
503                    for (int i = 1; i < n; i++) {
504                        SkPoint loc;
505                        SkEvalCubicAt(pts, t, &loc, NULL, NULL);
506                        t += dt;
507                        vert->setPoint(loc);
508                        vert++;
509                    }
510                    vert->setPoint(pts[3]);
511                    vert++;
512                    break;
513                }
514                case SkPath::kClose_Verb:
515                break;
516                case SkPath::kDone_Verb:
517                goto FINISHED;
518        }
519    }
520FINISHED:
521    if (needEnd) {
522        SkGL::DrawVertices(vert - base, GL_TRIANGLE_FAN, base, texs,
523                           NULL, NULL, clipIter);
524    }
525}
526
527