summaryrefslogtreecommitdiffstats
path: root/skia/gl/SkGL.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'skia/gl/SkGL.cpp')
-rw-r--r--skia/gl/SkGL.cpp453
1 files changed, 453 insertions, 0 deletions
diff --git a/skia/gl/SkGL.cpp b/skia/gl/SkGL.cpp
new file mode 100644
index 0000000..e4effba
--- /dev/null
+++ b/skia/gl/SkGL.cpp
@@ -0,0 +1,453 @@
+#include "SkGL.h"
+#include "SkColorPriv.h"
+#include "SkGeometry.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkTemplates.h"
+#include "SkXfermode.h"
+
+//#define TRACE_TEXTURE_CREATION
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_GL_HAS_COLOR4UB
+static inline void gl_pmcolor(U8CPU r, U8CPU g, U8CPU b, U8CPU a) {
+ glColor4ub(r, g, b, a);
+}
+
+void SkGL::SetAlpha(U8CPU alpha) {
+ glColor4ub(alpha, alpha, alpha, alpha);
+}
+#else
+static inline SkFixed byte2fixed(U8CPU value) {
+ return (value + (value >> 7)) << 8;
+}
+
+static inline void gl_pmcolor(U8CPU r, U8CPU g, U8CPU b, U8CPU a) {
+ glColor4x(byte2fixed(r), byte2fixed(g), byte2fixed(b), byte2fixed(a));
+}
+
+void SkGL::SetAlpha(U8CPU alpha) {
+ SkFixed fa = byte2fixed(alpha);
+ glColor4x(fa, fa, fa, fa);
+}
+#endif
+
+void SkGL::SetColor(SkColor c) {
+ SkPMColor pm = SkPreMultiplyColor(c);
+ gl_pmcolor(SkGetPackedR32(pm),
+ SkGetPackedG32(pm),
+ SkGetPackedB32(pm),
+ SkGetPackedA32(pm));
+}
+
+static const GLenum gXfermodeCoeff2Blend[] = {
+ GL_ZERO,
+ GL_ONE,
+ GL_SRC_COLOR,
+ GL_ONE_MINUS_SRC_COLOR,
+ GL_DST_COLOR,
+ GL_ONE_MINUS_DST_COLOR,
+ GL_SRC_ALPHA,
+ GL_ONE_MINUS_SRC_ALPHA,
+ GL_DST_ALPHA,
+ GL_ONE_MINUS_DST_ALPHA,
+};
+
+void SkGL::SetPaint(const SkPaint& paint, bool isPremul, bool justAlpha) {
+ if (justAlpha) {
+ SkGL::SetAlpha(paint.getAlpha());
+ } else {
+ SkGL::SetColor(paint.getColor());
+ }
+
+ GLenum sm = GL_ONE;
+ GLenum dm = GL_ONE_MINUS_SRC_ALPHA;
+
+ SkXfermode* mode = paint.getXfermode();
+ SkXfermode::Coeff sc, dc;
+ if (mode && mode->asCoeff(&sc, &dc)) {
+ sm = gXfermodeCoeff2Blend[sc];
+ dm = gXfermodeCoeff2Blend[dc];
+ }
+
+ // hack for text, which is not-premul (afaik)
+ if (!isPremul) {
+ if (GL_ONE == sm) {
+ sm = GL_SRC_ALPHA;
+ }
+ }
+
+ glEnable(GL_BLEND);
+ glBlendFunc(sm, dm);
+
+ if (paint.isDither()) {
+ glEnable(GL_DITHER);
+ } else {
+ glDisable(GL_DITHER);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGL::DumpError(const char caller[]) {
+ GLenum err = glGetError();
+ if (err) {
+ SkDebugf("---- glGetError(%s) %d\n", caller, err);
+ }
+}
+
+void SkGL::SetRGBA(uint8_t rgba[], const SkColor src[], int count) {
+ for (int i = 0; i < count; i++) {
+ SkPMColor c = SkPreMultiplyColor(*src++);
+ *rgba++ = SkGetPackedR32(c);
+ *rgba++ = SkGetPackedG32(c);
+ *rgba++ = SkGetPackedB32(c);
+ *rgba++ = SkGetPackedA32(c);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGL::Scissor(const SkIRect& r, int viewportHeight) {
+ glScissor(r.fLeft, viewportHeight - r.fBottom, r.width(), r.height());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGL::Ortho(float left, float right, float bottom, float top,
+ float near, float far) {
+
+ float mat[16];
+
+ bzero(mat, sizeof(mat));
+
+ mat[0] = 2 / (right - left);
+ mat[5] = 2 / (top - bottom);
+ mat[10] = 2 / (near - far);
+ mat[15] = 1;
+
+ mat[12] = (right + left) / (left - right);
+ mat[13] = (top + bottom) / (bottom - top);
+ mat[14] = (far + near) / (near - far);
+
+ glMultMatrixf(mat);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool canBeTexture(const SkBitmap& bm, GLenum* format, GLenum* type) {
+ switch (bm.config()) {
+ case SkBitmap::kARGB_8888_Config:
+ *format = GL_RGBA;
+ *type = GL_UNSIGNED_BYTE;
+ break;
+ case SkBitmap::kRGB_565_Config:
+ *format = GL_RGB;
+ *type = GL_UNSIGNED_SHORT_5_6_5;
+ break;
+ case SkBitmap::kARGB_4444_Config:
+ *format = GL_RGBA;
+ *type = GL_UNSIGNED_SHORT_4_4_4_4;
+ break;
+ case SkBitmap::kIndex8_Config:
+ // we promote index to argb32
+ *format = GL_RGBA;
+ *type = GL_UNSIGNED_BYTE;
+ break;
+ case SkBitmap::kA8_Config:
+ *format = GL_ALPHA;
+ *type = GL_UNSIGNED_BYTE;
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+size_t SkGL::ComputeTextureMemorySize(const SkBitmap& bitmap) {
+ int shift = 0;
+ switch (bitmap.config()) {
+ case SkBitmap::kARGB_8888_Config:
+ case SkBitmap::kRGB_565_Config:
+ case SkBitmap::kARGB_4444_Config:
+ case SkBitmap::kA8_Config:
+ // we're good as is
+ break;
+ case SkBitmap::kIndex8_Config:
+ // we promote index to argb32
+ shift = 2;
+ break;
+ default:
+ return 0;
+ }
+ return bitmap.getSize() << shift;
+}
+
+GLuint SkGL::BindNewTexture(const SkBitmap& origBitmap, SkPoint* max) {
+ SkBitmap tmpBitmap;
+ const SkBitmap* bitmap = &origBitmap;
+
+ if (origBitmap.config() == SkBitmap::kIndex8_Config) {
+ // we promote index to argb32
+ origBitmap.copyTo(&tmpBitmap, SkBitmap::kARGB_8888_Config);
+ bitmap = &tmpBitmap;
+ }
+
+ GLenum format, type;
+ if (!canBeTexture(*bitmap, &format, &type)) {
+ return 0;
+ }
+
+ SkAutoLockPixels alp(*bitmap);
+ if (bitmap->getPixels() == NULL) {
+ return 0;
+ }
+
+ GLuint textureName;
+ glGenTextures(1, &textureName);
+
+ glBindTexture(GL_TEXTURE_2D, textureName);
+
+ // express rowbytes as a number of pixels for ow
+ int ow = bitmap->rowBytesAsPixels();
+ int oh = bitmap->height();
+ int nw = SkNextPow2(ow);
+ int nh = SkNextPow2(oh);
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
+
+ // check if we need to scale to create power-of-2 dimensions
+ if (ow != nw || oh != nh) {
+ glTexImage2D(GL_TEXTURE_2D, 0, format, nw, nh, 0,
+ format, type, NULL);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, ow, oh,
+ format, type, bitmap->getPixels());
+ } else {
+ // easy case, the bitmap is already pow2
+ glTexImage2D(GL_TEXTURE_2D, 0, format, ow, oh, 0,
+ format, type, bitmap->getPixels());
+ }
+
+#ifdef TRACE_TEXTURE_CREATION
+ SkDebugf("--- new texture [%d] size=(%d %d) bpp=%d\n", textureName, ow, oh,
+ bitmap->bytesPerPixel());
+#endif
+
+ if (max) {
+ max->fX = SkFixedToScalar(bitmap->width() << (16 - SkNextLog2(nw)));
+ max->fY = SkFixedToScalar(oh << (16 - SkNextLog2(nh)));
+ }
+ return textureName;
+}
+
+static const GLenum gTileMode2GLWrap[] = {
+ GL_CLAMP_TO_EDGE,
+ GL_REPEAT,
+#if GL_VERSION_ES_CM_1_0
+ GL_REPEAT // GLES doesn't support MIRROR
+#else
+ GL_MIRRORED_REPEAT
+#endif
+};
+
+void SkGL::SetTexParams(bool doFilter,
+ SkShader::TileMode tx, SkShader::TileMode ty) {
+ SkASSERT((unsigned)tx < SK_ARRAY_COUNT(gTileMode2GLWrap));
+ SkASSERT((unsigned)ty < SK_ARRAY_COUNT(gTileMode2GLWrap));
+
+ GLenum filter = doFilter ? GL_LINEAR : GL_NEAREST;
+
+ SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
+ SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
+ SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gTileMode2GLWrap[tx]);
+ SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gTileMode2GLWrap[ty]);
+}
+
+void SkGL::SetTexParamsClamp(bool doFilter) {
+ GLenum filter = doFilter ? GL_LINEAR : GL_NEAREST;
+
+ SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
+ SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
+ SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGL::DrawVertices(int count, GLenum mode,
+ const SkGLVertex* SK_RESTRICT vertex,
+ const SkGLVertex* SK_RESTRICT texCoords,
+ const uint8_t* SK_RESTRICT colorArray,
+ const uint16_t* SK_RESTRICT indexArray,
+ SkGLClipIter* iter) {
+ SkASSERT(NULL != vertex);
+
+ if (NULL != texCoords) {
+ glEnable(GL_TEXTURE_2D);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glTexCoordPointer(2, SK_GLType, 0, texCoords);
+ } else {
+ glDisable(GL_TEXTURE_2D);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ }
+
+ if (NULL != colorArray) {
+ glEnableClientState(GL_COLOR_ARRAY);
+ glColorPointer(4, GL_UNSIGNED_BYTE, 0, colorArray);
+ glShadeModel(GL_SMOOTH);
+ } else {
+ glDisableClientState(GL_COLOR_ARRAY);
+ glShadeModel(GL_FLAT);
+ }
+
+ glVertexPointer(2, SK_GLType, 0, vertex);
+
+ if (NULL != indexArray) {
+ if (iter) {
+ while (!iter->done()) {
+ iter->scissor();
+ glDrawElements(mode, count, GL_UNSIGNED_SHORT, indexArray);
+ iter->next();
+ }
+ } else {
+ glDrawElements(mode, count, GL_UNSIGNED_SHORT, indexArray);
+ }
+ } else {
+ if (iter) {
+ while (!iter->done()) {
+ iter->scissor();
+ glDrawArrays(mode, 0, count);
+ iter->next();
+ }
+ } else {
+ glDrawArrays(mode, 0, count);
+ }
+ }
+}
+
+void SkGL::PrepareForFillPath(SkPaint* paint) {
+ if (paint->getStrokeWidth() <= 0) {
+ paint->setStrokeWidth(SK_Scalar1);
+ }
+}
+
+void SkGL::FillPath(const SkPath& path, const SkPaint& paint, bool useTex,
+ SkGLClipIter* iter) {
+ SkPaint p(paint);
+ SkPath fillPath;
+
+ SkGL::PrepareForFillPath(&p);
+ p.getFillPath(path, &fillPath);
+ SkGL::DrawPath(fillPath, useTex, iter);
+}
+
+// should return max of all contours, rather than the sum (to save temp RAM)
+static int worst_case_edge_count(const SkPath& path) {
+ int edgeCount = 0;
+
+ SkPath::Iter iter(path, true);
+ SkPath::Verb verb;
+
+ while ((verb = iter.next(NULL)) != SkPath::kDone_Verb) {
+ switch (verb) {
+ case SkPath::kLine_Verb:
+ edgeCount += 1;
+ break;
+ case SkPath::kQuad_Verb:
+ edgeCount += 8;
+ break;
+ case SkPath::kCubic_Verb:
+ edgeCount += 16;
+ break;
+ default:
+ break;
+ }
+ }
+ return edgeCount;
+}
+
+void SkGL::DrawPath(const SkPath& path, bool useTex, SkGLClipIter* clipIter) {
+ SkRect bounds;
+
+ path.computeBounds(&bounds, SkPath::kFast_BoundsType);
+ if (bounds.isEmpty()) {
+ return;
+ }
+
+ int maxPts = worst_case_edge_count(path);
+ // add 1 for center of fan, and 1 for closing edge
+ SkAutoSTMalloc<32, SkGLVertex> storage(maxPts + 2);
+ SkGLVertex* base = storage.get();
+ SkGLVertex* vert = base;
+ SkGLVertex* texs = useTex ? base : NULL;
+
+ SkPath::Iter pathIter(path, true);
+ SkPoint pts[4];
+
+ bool needEnd = false;
+
+ for (;;) {
+ switch (pathIter.next(pts)) {
+ case SkPath::kMove_Verb:
+ if (needEnd) {
+ SkGL::DrawVertices(vert - base, GL_TRIANGLE_FAN,
+ base, texs, NULL, NULL, clipIter);
+ clipIter->safeRewind();
+ vert = base;
+ }
+ needEnd = true;
+ // center of the FAN
+ vert->setScalars(bounds.centerX(), bounds.centerY());
+ vert++;
+ // add first edge point
+ vert->setPoint(pts[0]);
+ vert++;
+ break;
+ case SkPath::kLine_Verb:
+ vert->setPoint(pts[1]);
+ vert++;
+ break;
+ case SkPath::kQuad_Verb: {
+ const int n = 8;
+ const SkScalar dt = SK_Scalar1 / n;
+ SkScalar t = dt;
+ for (int i = 1; i < n; i++) {
+ SkPoint loc;
+ SkEvalQuadAt(pts, t, &loc, NULL);
+ t += dt;
+ vert->setPoint(loc);
+ vert++;
+ }
+ vert->setPoint(pts[2]);
+ vert++;
+ break;
+ }
+ case SkPath::kCubic_Verb: {
+ const int n = 16;
+ const SkScalar dt = SK_Scalar1 / n;
+ SkScalar t = dt;
+ for (int i = 1; i < n; i++) {
+ SkPoint loc;
+ SkEvalCubicAt(pts, t, &loc, NULL, NULL);
+ t += dt;
+ vert->setPoint(loc);
+ vert++;
+ }
+ vert->setPoint(pts[3]);
+ vert++;
+ break;
+ }
+ case SkPath::kClose_Verb:
+ break;
+ case SkPath::kDone_Verb:
+ goto FINISHED;
+ }
+ }
+FINISHED:
+ if (needEnd) {
+ SkGL::DrawVertices(vert - base, GL_TRIANGLE_FAN, base, texs,
+ NULL, NULL, clipIter);
+ }
+}
+