summaryrefslogtreecommitdiffstats
path: root/skia/gl
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-27 00:09:42 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-27 00:09:42 +0000
commitae2c20f398933a9e86c387dcc465ec0f71065ffc (patch)
treede668b1411e2ee0b4e49b6d8f8b68183134ac990 /skia/gl
parent09911bf300f1a419907a9412154760efd0b7abc3 (diff)
downloadchromium_src-ae2c20f398933a9e86c387dcc465ec0f71065ffc.zip
chromium_src-ae2c20f398933a9e86c387dcc465ec0f71065ffc.tar.gz
chromium_src-ae2c20f398933a9e86c387dcc465ec0f71065ffc.tar.bz2
Add skia to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@16 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'skia/gl')
-rw-r--r--skia/gl/SkGL.cpp453
-rw-r--r--skia/gl/SkGL.h307
-rw-r--r--skia/gl/SkGLCanvas.cpp143
-rw-r--r--skia/gl/SkGLDevice.cpp774
-rw-r--r--skia/gl/SkGLDevice.h121
-rw-r--r--skia/gl/SkGLDevice_FBO.cpp57
-rw-r--r--skia/gl/SkGLDevice_FBO.h23
-rw-r--r--skia/gl/SkGLDevice_SWLayer.cpp91
-rw-r--r--skia/gl/SkGLDevice_SWLayer.h49
-rw-r--r--skia/gl/SkGLTextCache.cpp201
-rw-r--r--skia/gl/SkGLTextCache.h83
-rw-r--r--skia/gl/SkTextureCache.cpp362
-rw-r--r--skia/gl/SkTextureCache.h160
13 files changed, 2824 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);
+ }
+}
+
diff --git a/skia/gl/SkGL.h b/skia/gl/SkGL.h
new file mode 100644
index 0000000..cf3c65e
--- /dev/null
+++ b/skia/gl/SkGL.h
@@ -0,0 +1,307 @@
+#ifndef SkGL_DEFINED
+#define SkGL_DEFINED
+
+#ifdef SK_BUILD_FOR_MAC
+ #include <OpenGL/gl.h>
+ #include <OpenGL/glext.h>
+ #include <AGL/agl.h>
+ // use FBOs for devices
+ #define SK_GL_DEVICE_FBO
+#elif defined(ANDROID)
+ #include <GLES/gl.h>
+ #include <GLES/egl.h>
+#endif
+
+#include "SkColor.h"
+#include "SkMatrix.h"
+#include "SkShader.h"
+
+class SkPaint;
+class SkPath;
+
+class SkGLClipIter;
+
+//#define TRACE_TEXTURE_CREATE
+
+static inline void* SkGetGLContext() {
+#ifdef ANDROID
+ return (void*)eglGetCurrentContext();
+#else
+ return NULL;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if GL_OES_fixed_point && defined(SK_SCALAR_IS_FIXED)
+ #define SK_GLType GL_FIXED
+#else
+ #define SK_GLType GL_FLOAT
+#endif
+
+#if SK_GLType == GL_FIXED
+ typedef SkFixed SkGLScalar;
+
+ #define SkIntToGL(n) SkIntToFixed(n)
+ #define SkScalarToGL(x) SkScalarToFixed(x)
+ #define SK_GLScalar1 SK_Fixed1
+ #define SkGLScalarMul(a, b) SkFixedMul(a, b)
+ #define MAKE_GL(name) name ## x
+
+ #ifdef SK_SCALAR_IS_FIXED
+ #define GLSCALAR_IS_SCALAR 1
+ #define SkPerspToGL(x) SkFractToFixed(x)
+ #else
+ #define GLSCALAR_IS_SCALAR 0
+ #define SkPerspToGL(x) SkFractToFloat(x)
+ #endif
+#else
+ typedef float SkGLScalar;
+
+ #define SkIntToGL(n) (n)
+ #define SkScalarToGL(x) SkScalarToFloat(x)
+ #define SK_GLScalar1 (1.f)
+ #define SkGLScalarMul(a, b) ((a) * (b))
+ #define MAKE_GL(name) name ## f
+
+ #ifdef SK_SCALAR_IS_FLOAT
+ #define GLSCALAR_IS_SCALAR 1
+ #define SkPerspToGL(x) (x)
+ #else
+ #define GLSCALAR_IS_SCALAR 0
+ #define SkPerspToGL(x) SkFractToFloat(x)
+ #endif
+#endif
+
+#if GL_OES_fixed_point
+ typedef SkFixed SkGLTextScalar;
+ #define SK_TextGLType GL_FIXED
+
+ #define SkIntToTextGL(n) SkIntToFixed(n)
+ #define SkFixedToTextGL(x) (x)
+
+ #define SK_glTexParameteri(target, pname, param) \
+ glTexParameterx(target, pname, param)
+#else
+ typedef float SkGLTextScalar;
+ #define SK_TextGLType SK_GLType
+ #define SK_GL_HAS_COLOR4UB
+
+ #define SkIntToTextGL(n) SkIntToGL(n)
+ #define SkFixedToTextGL(x) SkFixedToFloat(x)
+
+
+ #define SK_glTexParameteri(target, pname, param) \
+ glTexParameteri(target, pname, param)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+// text has its own vertex class, since it may want to be in fixed point (given)
+// that it starts with all integers) even when the default vertices are floats
+struct SkGLTextVertex {
+ SkGLTextScalar fX;
+ SkGLTextScalar fY;
+
+ void setI(int x, int y) {
+ fX = SkIntToTextGL(x);
+ fY = SkIntToTextGL(y);
+ }
+
+ void setX(SkFixed x, SkFixed y) {
+ fX = SkFixedToTextGL(x);
+ fY = SkFixedToTextGL(y);
+ }
+
+ // counter-clockwise fan
+ void setIRectFan(int l, int t, int r, int b) {
+ SkGLTextVertex* SK_RESTRICT v = this;
+ v[0].setI(l, t);
+ v[1].setI(l, b);
+ v[2].setI(r, b);
+ v[3].setI(r, t);
+ }
+
+ // counter-clockwise fan
+ void setXRectFan(SkFixed l, SkFixed t, SkFixed r, SkFixed b) {
+ SkGLTextVertex* SK_RESTRICT v = this;
+ v[0].setX(l, t);
+ v[1].setX(l, b);
+ v[2].setX(r, b);
+ v[3].setX(r, t);
+ }
+};
+
+struct SkGLVertex {
+ SkGLScalar fX;
+ SkGLScalar fY;
+
+ void setGL(SkGLScalar x, SkGLScalar y) {
+ fX = x;
+ fY = y;
+ }
+
+ void setScalars(SkScalar x, SkScalar y) {
+ fX = SkScalarToGL(x);
+ fY = SkScalarToGL(y);
+ }
+
+ void setPoint(const SkPoint& pt) {
+ fX = SkScalarToGL(pt.fX);
+ fY = SkScalarToGL(pt.fY);
+ }
+
+ void setPoints(const SkPoint* SK_RESTRICT pts, int count) {
+ const SkScalar* SK_RESTRICT src = (const SkScalar*)pts;
+ SkGLScalar* SK_RESTRICT dst = (SkGLScalar*)this;
+ for (int i = 0; i < count; i++) {
+ *dst++ = SkScalarToGL(*src++);
+ *dst++ = SkScalarToGL(*src++);
+ }
+ }
+
+ // counter-clockwise fan
+ void setRectFan(SkScalar l, SkScalar t, SkScalar r, SkScalar b) {
+ SkGLVertex* v = this;
+ v[0].setScalars(l, t);
+ v[1].setScalars(l, b);
+ v[2].setScalars(r, b);
+ v[3].setScalars(r, t);
+ }
+
+ // counter-clockwise fan
+ void setIRectFan(int l, int t, int r, int b) {
+ SkGLVertex* v = this;
+ v[0].setGL(SkIntToGL(l), SkIntToGL(t));
+ v[1].setGL(SkIntToGL(l), SkIntToGL(b));
+ v[2].setGL(SkIntToGL(r), SkIntToGL(b));
+ v[3].setGL(SkIntToGL(r), SkIntToGL(t));
+ }
+
+ // counter-clockwise fan
+ void setRectFan(const SkRect& r) {
+ this->setRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom);
+ }
+
+ // counter-clockwise fan
+ void setIRectFan(const SkIRect& r) {
+ this->setIRectFan(r.fLeft, r.fTop, r.fRight, r.fBottom);
+ }
+};
+
+struct SkGLMatrix {
+ SkGLScalar fMat[16];
+
+ void reset() {
+ bzero(fMat, sizeof(fMat));
+ fMat[0] = fMat[5] = fMat[10] = fMat[15] = SK_GLScalar1;
+ }
+
+ void set(const SkMatrix& m) {
+ bzero(fMat, sizeof(fMat));
+ fMat[0] = SkScalarToGL(m[SkMatrix::kMScaleX]);
+ fMat[4] = SkScalarToGL(m[SkMatrix::kMSkewX]);
+ fMat[12] = SkScalarToGL(m[SkMatrix::kMTransX]);
+
+ fMat[1] = SkScalarToGL(m[SkMatrix::kMSkewY]);
+ fMat[5] = SkScalarToGL(m[SkMatrix::kMScaleY]);
+ fMat[13] = SkScalarToGL(m[SkMatrix::kMTransY]);
+
+ fMat[3] = SkPerspToGL(m[SkMatrix::kMPersp0]);
+ fMat[7] = SkPerspToGL(m[SkMatrix::kMPersp1]);
+ fMat[15] = SkPerspToGL(m[SkMatrix::kMPersp2]);
+
+ fMat[10] = SK_GLScalar1; // z-scale
+ }
+};
+
+class SkGL {
+public:
+ static void SetColor(SkColor c);
+ static void SetAlpha(U8CPU alpha);
+ static void SetPaint(const SkPaint&, bool isPremul = true,
+ bool justAlpha = false);
+ static void SetPaintAlpha(const SkPaint& paint, bool isPremul = true) {
+ SetPaint(paint, isPremul, true);
+ }
+
+ static void SetRGBA(uint8_t rgba[], const SkColor src[], int count);
+ static void DumpError(const char caller[]);
+
+ static void Ortho(float left, float right, float bottom, float top,
+ float near, float far);
+
+ static inline void Translate(SkScalar dx, SkScalar dy) {
+ MAKE_GL(glTranslate)(SkScalarToGL(dx), SkScalarToGL(dy), 0);
+ }
+
+ static inline void Scale(SkScalar sx, SkScalar sy) {
+ MAKE_GL(glScale)(SkScalarToGL(sx), SkScalarToGL(sy), SK_GLScalar1);
+ }
+
+ static inline void Rotate(SkScalar angle) {
+ MAKE_GL(glRotate)(SkScalarToGL(angle), 0, 0, SK_GLScalar1);
+ }
+
+ static inline void MultMatrix(const SkMatrix& m) {
+ SkGLMatrix glm;
+ glm.set(m);
+ MAKE_GL(glMultMatrix)(glm.fMat);
+ }
+
+ static inline void LoadMatrix(const SkMatrix& m) {
+ SkGLMatrix glm;
+ glm.set(m);
+ MAKE_GL(glLoadMatrix)(glm.fMat);
+ }
+
+ static void Scissor(const SkIRect&, int viewportHeight);
+
+ // return the byte size for the associated texture memory. This doesn't
+ // always == bitmap.getSize(), since on a given port we may have to change
+ // the format when the bitmap's pixels are copied over to GL
+ static size_t ComputeTextureMemorySize(const SkBitmap&);
+ // return 0 on failure
+ static GLuint BindNewTexture(const SkBitmap&, SkPoint* dimension);
+
+ static void SetTexParams(bool filter,
+ SkShader::TileMode tx, SkShader::TileMode ty);
+ static void SetTexParamsClamp(bool filter);
+
+ static void 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*);
+
+ static void PrepareForFillPath(SkPaint* paint);
+ static void FillPath(const SkPath& path, const SkPaint& paint, bool useTex,
+ SkGLClipIter*);
+ static void DrawPath(const SkPath& path, bool useTex, SkGLClipIter*);
+};
+
+#include "SkRegion.h"
+
+class SkGLClipIter : public SkRegion::Iterator {
+public:
+ SkGLClipIter(int viewportHeight) : fViewportHeight(viewportHeight) {}
+
+ // call rewind only if this is non-null
+ void safeRewind() {
+ if (this) {
+ this->rewind();
+ }
+ }
+
+ void scissor() {
+ SkASSERT(!this->done());
+ SkGL::Scissor(this->rect(), fViewportHeight);
+ }
+
+private:
+ const int fViewportHeight;
+};
+
+#endif
+
diff --git a/skia/gl/SkGLCanvas.cpp b/skia/gl/SkGLCanvas.cpp
new file mode 100644
index 0000000..d9f2201
--- /dev/null
+++ b/skia/gl/SkGLCanvas.cpp
@@ -0,0 +1,143 @@
+#include "SkGLCanvas.h"
+#include "SkGLDevice.h"
+#include "SkBlitter.h"
+#include "SkDraw.h"
+#include "SkDrawProcs.h"
+#include "SkGL.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+#include "SkXfermode.h"
+
+#ifdef SK_GL_DEVICE_FBO
+ #define USE_FBO_DEVICE
+ #include "SkGLDevice_FBO.h"
+#else
+ #define USE_SWLAYER_DEVICE
+ #include "SkGLDevice_SWLayer.h"
+#endif
+
+// maximum number of entries in our texture cache (before purging)
+#define kTexCountMax_Default 256
+// maximum number of bytes used (by gl) for the texture cache (before purging)
+#define kTexSizeMax_Default (4 * 1024 * 1024)
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGLCanvas::SkGLCanvas() {
+ glEnable(GL_TEXTURE_2D);
+ glEnable(GL_SCISSOR_TEST);
+ glEnableClientState(GL_VERTEX_ARRAY);
+
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ fViewportSize.set(0, 0);
+}
+
+SkGLCanvas::~SkGLCanvas() {
+ // call this now, while our override of restore() is in effect
+ this->restoreToCount(1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkGLCanvas::getViewport(SkIPoint* size) const {
+ if (size) {
+ *size = fViewportSize;
+ }
+ return true;
+}
+
+bool SkGLCanvas::setViewport(int width, int height) {
+ fViewportSize.set(width, height);
+
+ const bool isOpaque = false; // should this be a parameter to setViewport?
+ const bool isForLayer = false; // viewport is the base layer
+ SkDevice* device = this->createDevice(SkBitmap::kARGB_8888_Config, width,
+ height, isOpaque, isForLayer);
+ this->setDevice(device)->unref();
+
+ return true;
+}
+
+SkDevice* SkGLCanvas::createDevice(SkBitmap::Config, int width, int height,
+ bool isOpaque, bool isForLayer) {
+ SkBitmap bitmap;
+
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ bitmap.setIsOpaque(isOpaque);
+
+#ifdef USE_FBO_DEVICE
+ return SkNEW_ARGS(SkGLDevice_FBO, (bitmap, isForLayer));
+#elif defined(USE_SWLAYER_DEVICE)
+ if (isForLayer) {
+ bitmap.allocPixels();
+ if (!bitmap.isOpaque()) {
+ bitmap.eraseColor(0);
+ }
+ return SkNEW_ARGS(SkGLDevice_SWLayer, (bitmap));
+ } else {
+ return SkNEW_ARGS(SkGLDevice, (bitmap, isForLayer));
+ }
+#else
+ return SkNEW_ARGS(SkGLDevice, (bitmap, isForLayer));
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTextureCache.h"
+#include "SkThread.h"
+
+static SkMutex gTextureCacheMutex;
+static SkTextureCache gTextureCache(kTexCountMax_Default, kTexSizeMax_Default);
+static void* gTextureGLContext;
+
+SkGLDevice::TexCache* SkGLDevice::LockTexCache(const SkBitmap& bitmap,
+ GLuint* name, SkPoint* size) {
+ SkAutoMutexAcquire amc(gTextureCacheMutex);
+
+ void* ctx = SkGetGLContext();
+ if (gTextureGLContext != ctx) {
+ gTextureGLContext = ctx;
+ gTextureCache.zapAllTextures();
+ }
+
+ SkTextureCache::Entry* entry = gTextureCache.lock(bitmap);
+ if (NULL != entry) {
+ if (name) {
+ *name = entry->name();
+ }
+ if (size) {
+ *size = entry->texSize();
+ }
+ }
+ return (TexCache*)entry;
+}
+
+void SkGLDevice::UnlockTexCache(TexCache* cache) {
+ SkAutoMutexAcquire amc(gTextureCacheMutex);
+ gTextureCache.unlock((SkTextureCache::Entry*)cache);
+}
+
+// public exposure of texture cache settings
+
+size_t SkGLCanvas::GetTextureCacheMaxCount() {
+ SkAutoMutexAcquire amc(gTextureCacheMutex);
+ return gTextureCache.getMaxCount();
+}
+
+size_t SkGLCanvas::GetTextureCacheMaxSize() {
+ SkAutoMutexAcquire amc(gTextureCacheMutex);
+ return gTextureCache.getMaxSize();
+}
+
+void SkGLCanvas::SetTextureCacheMaxCount(size_t count) {
+ SkAutoMutexAcquire amc(gTextureCacheMutex);
+ gTextureCache.setMaxCount(count);
+}
+
+void SkGLCanvas::SetTextureCacheMaxSize(size_t size) {
+ SkAutoMutexAcquire amc(gTextureCacheMutex);
+ gTextureCache.setMaxSize(size);
+}
+
diff --git a/skia/gl/SkGLDevice.cpp b/skia/gl/SkGLDevice.cpp
new file mode 100644
index 0000000..2271b05
--- /dev/null
+++ b/skia/gl/SkGLDevice.cpp
@@ -0,0 +1,774 @@
+#include "SkGLDevice.h"
+#include "SkGL.h"
+#include "SkDrawProcs.h"
+#include "SkRegion.h"
+#include "SkThread.h"
+
+static void TRACE_DRAW(const char func[], SkGLDevice* device,
+ const SkDraw& draw) {
+ // SkDebugf("--- <%s> %p %p\n", func, canvas, draw.fDevice);
+}
+
+struct SkGLDrawProcs : public SkDrawProcs {
+public:
+ void init(const SkRegion* clip, int height) {
+ fCurrQuad = 0;
+ fCurrTexture = 0;
+ fClip = clip;
+ fViewportHeight = height;
+
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glTexCoordPointer(2, SK_TextGLType, 0, fTexs);
+ glDisableClientState(GL_COLOR_ARRAY);
+ glVertexPointer(2, SK_TextGLType, 0, fVerts);
+
+ fCtx = SkGetGLContext();
+ }
+
+ void* ctx() const { return fCtx; }
+ GLenum texture() const { return fCurrTexture; }
+
+ void flush() {
+ if (fCurrQuad && fCurrTexture) {
+ this->drawQuads();
+ }
+ fCurrQuad = 0;
+ }
+
+ void addQuad(GLuint texture, int x, int y, const SkGlyph& glyph,
+ SkFixed left, SkFixed right, SkFixed bottom) {
+ SkASSERT((size_t)fCurrQuad <= SK_ARRAY_COUNT(fVerts));
+
+ if (fCurrTexture != texture || fCurrQuad == SK_ARRAY_COUNT(fVerts)) {
+ if (fCurrQuad && fCurrTexture) {
+ this->drawQuads();
+ }
+ fCurrQuad = 0;
+ fCurrTexture = texture;
+ }
+
+ fVerts[fCurrQuad].setIRectFan(x, y,
+ x + glyph.fWidth, y + glyph.fHeight);
+ fTexs[fCurrQuad].setXRectFan(left, 0, right, bottom);
+ fCurrQuad += 4;
+ }
+
+ void drawQuads();
+
+private:
+ enum {
+ MAX_QUADS = 32
+ };
+
+ SkGLTextVertex fVerts[MAX_QUADS * 4];
+ SkGLTextVertex fTexs[MAX_QUADS * 4];
+
+ // these are initialized in setupForText
+ GLuint fCurrTexture;
+ int fCurrQuad;
+ int fViewportHeight;
+ const SkRegion* fClip;
+ void* fCtx;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGLDevice::SkGLDevice(const SkBitmap& bitmap, bool offscreen)
+ : SkDevice(bitmap), fClipIter(bitmap.height()) {
+ fDrawProcs = NULL;
+}
+
+SkGLDevice::~SkGLDevice() {
+ if (fDrawProcs) {
+ SkDELETE(fDrawProcs);
+ }
+}
+
+void SkGLDevice::setMatrixClip(const SkMatrix& matrix, const SkRegion& clip) {
+ this->INHERITED::setMatrixClip(matrix, clip);
+
+ fGLMatrix.set(matrix);
+ fMatrix = matrix;
+ fClip = clip;
+ fDirty = true;
+}
+
+SkGLDevice::TexOrientation SkGLDevice::bindDeviceAsTexture() {
+ return kNo_TexOrientation;
+}
+
+void SkGLDevice::gainFocus(SkCanvas* canvas) {
+ this->INHERITED::gainFocus(canvas);
+
+ const int w = this->width();
+ const int h = this->height();
+ glViewport(0, 0, w, h);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ SkGL::Ortho(0, w, h, 0, -1, 1);
+ glMatrixMode(GL_MODELVIEW);
+ fDirty = true;
+}
+
+SkGLClipIter* SkGLDevice::updateMatrixClip() {
+ bool useIter = false;
+
+ // first handle the clip
+ if (fDirty || !fClip.isRect()) {
+ fClipIter.reset(fClip);
+ useIter = true;
+ } else if (fDirty) {
+ // no iter means caller is not respecting complex clips :(
+ SkGL::Scissor(fClip.getBounds(), this->height());
+ }
+ // else we're just a rect, and we've already call scissor
+
+ // now handle the matrix
+ if (fDirty) {
+ MAKE_GL(glLoadMatrix)(fGLMatrix.fMat);
+#if 0
+ SkDebugf("--- gldevice update matrix %p %p\n", this, fFBO);
+ for (int y = 0; y < 4; y++) {
+ SkDebugf(" [ ");
+ for (int x = 0; x < 4; x++) {
+ SkDebugf("%g ", fGLMatrix.fMat[y*4 + x]);
+ }
+ SkDebugf("]\n");
+ }
+#endif
+ fDirty = false;
+ }
+
+ return useIter ? &fClipIter : NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// must be in the same order as SkXfermode::Coeff in SkXfermode.h
+SkGLDevice::AutoPaintShader::AutoPaintShader(SkGLDevice* device,
+ const SkPaint& paint) {
+ fDevice = device;
+ fTexCache = device->setupGLPaintShader(paint);
+}
+
+SkGLDevice::AutoPaintShader::~AutoPaintShader() {
+ if (fTexCache) {
+ SkGLDevice::UnlockTexCache(fTexCache);
+ }
+}
+
+SkGLDevice::TexCache* SkGLDevice::setupGLPaintShader(const SkPaint& paint) {
+ SkGL::SetPaint(paint);
+
+ SkShader* shader = paint.getShader();
+ if (NULL == shader) {
+ return NULL;
+ }
+
+ if (!shader->setContext(this->accessBitmap(false), paint, this->matrix())) {
+ return NULL;
+ }
+
+ SkBitmap bitmap;
+ SkMatrix matrix;
+ SkShader::TileMode tileModes[2];
+ if (!shader->asABitmap(&bitmap, &matrix, tileModes)) {
+ return NULL;
+ }
+
+ bitmap.lockPixels();
+ if (bitmap.getPixels() == NULL) {
+ return NULL;
+ }
+
+ // see if we've already cached the bitmap from the shader
+ SkPoint max;
+ GLuint name;
+ TexCache* cache = SkGLDevice::LockTexCache(bitmap, &name, &max);
+ // the lock has already called glBindTexture for us
+ SkGL::SetTexParams(paint.isFilterBitmap(), tileModes[0], tileModes[1]);
+
+ // since our texture coords will be in local space, we wack the texture
+ // matrix to map them back into 0...1 before we load it
+ SkMatrix localM;
+ if (shader->getLocalMatrix(&localM)) {
+ SkMatrix inverse;
+ if (localM.invert(&inverse)) {
+ matrix.preConcat(inverse);
+ }
+ }
+
+ matrix.postScale(max.fX / bitmap.width(), max.fY / bitmap.height());
+ glMatrixMode(GL_TEXTURE);
+ SkGL::LoadMatrix(matrix);
+ glMatrixMode(GL_MODELVIEW);
+
+ // since we're going to use a shader/texture, we don't want the color,
+ // just its alpha
+ SkGL::SetAlpha(paint.getAlpha());
+ // report that we have setup the texture
+ return cache;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGLDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
+ TRACE_DRAW("coreDrawPaint", this, draw);
+
+ AutoPaintShader shader(this, paint);
+ SkGLVertex vertex[4];
+ const SkGLVertex* texs = shader.useTex() ? vertex : NULL;
+
+ // set vert to be big enough to fill the space, but not super-huge, to we
+ // don't overflow fixed-point implementations
+ {
+ SkRect r;
+ r.set(this->clip().getBounds());
+ SkMatrix inverse;
+ if (draw.fMatrix->invert(&inverse)) {
+ inverse.mapRect(&r);
+ }
+ vertex->setRectFan(r);
+ }
+
+ SkGL::DrawVertices(4, GL_TRIANGLE_FAN, vertex, texs, NULL, NULL,
+ this->updateMatrixClip());
+}
+
+static const GLenum gPointMode2GL[] = {
+ GL_POINTS,
+ GL_LINES,
+ GL_LINE_STRIP
+};
+
+void SkGLDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
+ size_t count, const SkPoint pts[], const SkPaint& paint) {
+ TRACE_DRAW("coreDrawPoints", this, draw);
+
+ SkScalar width = paint.getStrokeWidth();
+ if (width < 0) {
+ return;
+ }
+
+ /* We should really only use drawverts for hairlines, since gl and skia
+ treat the thickness differently...
+ */
+
+ AutoPaintShader shader(this, paint);
+
+ if (width <= 0) {
+ width = SK_Scalar1;
+ }
+
+ if (SkCanvas::kPoints_PointMode == mode) {
+ glPointSize(SkScalarToFloat(width));
+ } else {
+ glLineWidth(SkScalarToFloat(width));
+ }
+
+ const SkGLVertex* verts;
+
+#if GLSCALAR_IS_SCALAR
+ verts = (const SkGLVertex*)pts;
+#else
+ SkAutoSTMalloc<32, SkGLVertex> storage(count);
+ SkGLVertex* v = storage.get();
+
+ v->setPoints(pts, count);
+ verts = v;
+#endif
+
+ const SkGLVertex* texs = shader.useTex() ? verts : NULL;
+
+ SkGL::DrawVertices(count, gPointMode2GL[mode], verts, texs, NULL, NULL,
+ this->updateMatrixClip());
+}
+
+void SkGLDevice::drawRect(const SkDraw& draw, const SkRect& rect,
+ const SkPaint& paint) {
+ TRACE_DRAW("coreDrawRect", this, draw);
+
+ if (paint.getStyle() == SkPaint::kStroke_Style) {
+ return;
+ }
+
+ if (paint.getStrokeJoin() != SkPaint::kMiter_Join) {
+ SkPath path;
+ path.addRect(rect);
+ this->drawPath(draw, path, paint);
+ return;
+ }
+
+ AutoPaintShader shader(this, paint);
+
+ SkGLVertex vertex[4];
+ vertex->setRectFan(rect);
+ const SkGLVertex* texs = shader.useTex() ? vertex : NULL;
+
+ SkGL::DrawVertices(4, GL_TRIANGLE_FAN, vertex, texs, NULL, NULL,
+ this->updateMatrixClip());
+}
+
+void SkGLDevice::drawPath(const SkDraw& draw, const SkPath& path,
+ const SkPaint& paint) {
+ TRACE_DRAW("coreDrawPath", this, draw);
+ if (paint.getStyle() == SkPaint::kStroke_Style) {
+ return;
+ }
+
+ AutoPaintShader shader(this, paint);
+
+ SkGL::FillPath(path, paint, shader.useTex(), this->updateMatrixClip());
+}
+
+void SkGLDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
+ const SkMatrix& m, const SkPaint& paint) {
+ TRACE_DRAW("coreDrawBitmap", this, draw);
+
+ SkAutoLockPixels alp(bitmap);
+ if (bitmap.getPixels() == NULL) {
+ return;
+ }
+
+ SkGLClipIter* iter = this->updateMatrixClip();
+
+ SkPoint max;
+ GLenum name;
+ SkAutoLockTexCache(bitmap, &name, &max);
+ // the lock has already called glBindTexture for us
+ SkGL::SetTexParamsClamp(paint.isFilterBitmap());
+
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ SkGL::MultMatrix(m);
+
+ SkGLVertex pts[4], tex[4];
+
+ pts->setIRectFan(0, 0, bitmap.width(), bitmap.height());
+ tex->setRectFan(0, 0, max.fX, max.fY);
+
+ // now draw the mesh
+ SkGL::SetPaintAlpha(paint);
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ SkGL::DrawVertices(4, GL_TRIANGLE_FAN, pts, tex, NULL, NULL, iter);
+
+ glPopMatrix();
+}
+
+// move this guy into SkGL, so we can call it from SkGLDevice
+static void gl_drawSprite(int x, int y, int w, int h, const SkPoint& max,
+ const SkPaint& paint, SkGLClipIter* iter) {
+ SkGL::SetTexParamsClamp(false);
+
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ SkGLVertex pts[4], tex[4];
+
+ // if h < 0, then the texture is bottom-to-top, but since our projection
+ // matrix always inverts Y, we have to re-invert our texture coord here
+ if (h < 0) {
+ h = -h;
+ tex->setRectFan(0, max.fY, max.fX, 0);
+ } else {
+ tex->setRectFan(0, 0, max.fX, max.fY);
+ }
+ pts->setIRectFan(x, y, x + w, y + h);
+
+ SkGL::SetPaintAlpha(paint);
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ // should look to use glDrawTexi() has we do for text...
+ SkGL::DrawVertices(4, GL_TRIANGLE_FAN, pts, tex, NULL, NULL, iter);
+
+ glPopMatrix();
+}
+
+void SkGLDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
+ int left, int top, const SkPaint& paint) {
+ TRACE_DRAW("coreDrawSprite", this, draw);
+
+ SkAutoLockPixels alp(bitmap);
+ if (bitmap.getPixels() == NULL) {
+ return;
+ }
+
+ SkGLClipIter* iter = this->updateMatrixClip();
+
+ SkPoint max;
+ GLuint name;
+ SkAutoLockTexCache(bitmap, &name, &max);
+
+ gl_drawSprite(left, top, bitmap.width(), bitmap.height(), max, paint, iter);
+}
+
+void SkGLDevice::drawDevice(const SkDraw& draw, SkDevice* dev,
+ int x, int y, const SkPaint& paint) {
+ TRACE_DRAW("coreDrawDevice", this, draw);
+
+ SkGLDevice::TexOrientation to = ((SkGLDevice*)dev)->bindDeviceAsTexture();
+ if (SkGLDevice::kNo_TexOrientation != to) {
+ SkGLClipIter* iter = this->updateMatrixClip();
+
+ const SkBitmap& bm = dev->accessBitmap(false);
+ int w = bm.width();
+ int h = bm.height();
+ SkPoint max;
+
+ max.set(SkFixedToScalar(w << (16 - SkNextLog2(bm.rowBytesAsPixels()))),
+ SkFixedToScalar(h << (16 - SkNextLog2(h))));
+
+ if (SkGLDevice::kBottomToTop_TexOrientation == to) {
+ h = -h;
+ }
+ gl_drawSprite(x, y, w, h, max, paint, iter);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const GLenum gVertexModeToGL[] = {
+ GL_TRIANGLES, // kTriangles_VertexMode,
+ GL_TRIANGLE_STRIP, // kTriangleStrip_VertexMode,
+ GL_TRIANGLE_FAN // kTriangleFan_VertexMode
+};
+
+#include "SkShader.h"
+
+void SkGLDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
+ int vertexCount, const SkPoint vertices[],
+ const SkPoint texs[], const SkColor colors[],
+ SkXfermode* xmode,
+ const uint16_t indices[], int indexCount,
+ const SkPaint& paint) {
+
+ if (false) {
+ SkRect bounds;
+ SkIRect ibounds;
+
+ bounds.set(vertices, vertexCount);
+ bounds.round(&ibounds);
+
+ SkDebugf("---- drawverts: %d pts, texs=%d colors=%d indices=%d bounds [%d %d]\n",
+ vertexCount, texs!=0, colors!=0, indexCount, ibounds.width(), ibounds.height());
+ }
+
+ SkGLClipIter* iter = this->updateMatrixClip();
+
+ SkGL::SetPaint(paint);
+
+ const SkGLVertex* glVerts;
+ const SkGLVertex* glTexs = NULL;
+
+#if GLSCALAR_IS_SCALAR
+ glVerts = (const SkGLVertex*)vertices;
+#else
+ SkAutoSTMalloc<32, SkGLVertex> storage(vertexCount);
+ storage.get()->setPoints(vertices, vertexCount);
+ glVerts = storage.get();
+#endif
+
+ uint8_t* colorArray = NULL;
+ if (colors) {
+ colorArray = (uint8_t*)sk_malloc_throw(vertexCount*4);
+ SkGL::SetRGBA(colorArray, colors, vertexCount);
+ }
+ SkAutoFree afca(colorArray);
+
+ SkGLVertex* texArray = NULL;
+ TexCache* cache = NULL;
+
+ if (texs && paint.getShader()) {
+ SkShader* shader = paint.getShader();
+
+ // if (!shader->setContext(this->accessBitmap(), paint, *draw.fMatrix)) {
+ if (!shader->setContext(*draw.fBitmap, paint, *draw.fMatrix)) {
+ goto DONE;
+ }
+
+ SkBitmap bitmap;
+ SkMatrix matrix;
+ SkShader::TileMode tileModes[2];
+ if (shader->asABitmap(&bitmap, &matrix, tileModes)) {
+ SkPoint max;
+ GLuint name;
+ cache = SkGLDevice::LockTexCache(bitmap, &name, &max);
+ if (NULL == cache) {
+ return;
+ }
+
+ matrix.postScale(max.fX / bitmap.width(), max.fY / bitmap.height());
+ glMatrixMode(GL_TEXTURE);
+ SkGL::LoadMatrix(matrix);
+ glMatrixMode(GL_MODELVIEW);
+
+#if GLSCALAR_IS_SCALAR
+ glTexs = (const SkGLVertex*)texs;
+#else
+ texArray = (SkGLVertex*)sk_malloc_throw(vertexCount * sizeof(SkGLVertex));
+ texArray->setPoints(texs, vertexCount);
+ glTexs = texArray;
+#endif
+
+ SkGL::SetPaintAlpha(paint);
+ SkGL::SetTexParams(paint.isFilterBitmap(),
+ tileModes[0], tileModes[1]);
+ }
+ }
+DONE:
+ SkAutoFree aftex(texArray);
+
+ SkGL::DrawVertices(indices ? indexCount : vertexCount,
+ gVertexModeToGL[vmode],
+ glVerts, glTexs, colorArray, indices, iter);
+
+ if (cache) {
+ SkGLDevice::UnlockTexCache(cache);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkGlyphCache.h"
+#include "SkGLTextCache.h"
+
+static void SkGL_GlyphCacheAuxProc(void* data) {
+ SkGLTextCache* cache = (SkGLTextCache*)data;
+
+ SkDebugf("-------------- delete text texture cache, ctx=%p\n",
+ cache->getCtx());
+ SkDELETE((SkGLTextCache*)data);
+}
+
+#ifdef SK_SCALAR_IS_FIXED
+#define SkDiv16ToScalar(numer, denom) (SkIntToFixed(numer) / (denom))
+#else
+#define SkDiv16ToScalar(numer, denom) SkScalarDiv(numer, denom)
+#endif
+
+// stolen from SkDraw.cpp - D1G_NoBounder_RectClip
+static void SkGL_Draw1Glyph(const SkDraw1Glyph& state, const SkGlyph& glyph,
+ int x, int y) {
+ SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
+
+ SkGLDrawProcs* procs = (SkGLDrawProcs*)state.fDraw->fProcs;
+
+ x += glyph.fLeft;
+ y += glyph.fTop;
+
+ // check if we're clipped out (nothing to draw)
+ SkIRect bounds;
+ bounds.set(x, y, x + glyph.fWidth, y + glyph.fHeight);
+ if (!SkIRect::Intersects(state.fClip->getBounds(), bounds)) {
+ return;
+ }
+
+ // now dig up our texture cache
+
+ SkGlyphCache* gcache = state.fCache;
+ void* auxData;
+ SkGLTextCache* textCache = NULL;
+
+ if (gcache->getAuxProcData(SkGL_GlyphCacheAuxProc, &auxData)) {
+ textCache = (SkGLTextCache*)auxData;
+ if (textCache->getCtx() != procs->ctx()) {
+ SkDebugf("------- textcache: old ctx %p new ctx %p\n",
+ textCache->getCtx(), procs->ctx());
+ SkDELETE(textCache);
+ textCache = NULL;
+ }
+ }
+ if (NULL == textCache) {
+ // need to create one
+ textCache = SkNEW(SkGLTextCache);
+ gcache->setAuxProc(SkGL_GlyphCacheAuxProc, textCache);
+ }
+
+ int offset;
+ SkGLTextCache::Strike* strike = textCache->findGlyph(glyph, &offset);
+ if (NULL == strike) {
+ // make sure the glyph has an image
+ uint8_t* aa = (uint8_t*)glyph.fImage;
+ if (NULL == aa) {
+ aa = (uint8_t*)gcache->findImage(glyph);
+ if (NULL == aa) {
+ return; // can't rasterize glyph
+ }
+ }
+ strike = textCache->addGlyphAndBind(glyph, aa, &offset);
+ if (NULL == strike) {
+ // too big to cache, need to draw as is...
+ return;
+ }
+ }
+
+ const int shiftW = strike->widthShift();
+ const int shiftH = strike->heightShift();
+
+ SkFixed left = offset << (16 - shiftW);
+ SkFixed right = (offset + glyph.fWidth) << (16 - shiftW);
+ SkFixed bottom = glyph.fHeight << (16 - shiftH);
+
+ procs->addQuad(strike->texture(), x, y, glyph, left, right, bottom);
+}
+
+#if 1
+// matches the orientation used in SkGL::setRectFan. Too bad we can't rely on
+// QUADS in android's GL
+static const uint8_t gQuadIndices[] = {
+ 0, 1, 2, 0, 2, 3,
+ 4, 5, 6, 4, 6, 7,
+ 8, 9, 10, 8, 10, 11,
+ 12, 13, 14, 12, 14, 15,
+ 16, 17, 18, 16, 18, 19,
+ 20, 21, 22, 20, 22, 23,
+ 24, 25, 26, 24, 26, 27,
+ 28, 29, 30, 28, 30, 31,
+ 32, 33, 34, 32, 34, 35,
+ 36, 37, 38, 36, 38, 39,
+ 40, 41, 42, 40, 42, 43,
+ 44, 45, 46, 44, 46, 47,
+ 48, 49, 50, 48, 50, 51,
+ 52, 53, 54, 52, 54, 55,
+ 56, 57, 58, 56, 58, 59,
+ 60, 61, 62, 60, 62, 63,
+ 64, 65, 66, 64, 66, 67,
+ 68, 69, 70, 68, 70, 71,
+ 72, 73, 74, 72, 74, 75,
+ 76, 77, 78, 76, 78, 79,
+ 80, 81, 82, 80, 82, 83,
+ 84, 85, 86, 84, 86, 87,
+ 88, 89, 90, 88, 90, 91,
+ 92, 93, 94, 92, 94, 95,
+ 96, 97, 98, 96, 98, 99,
+ 100, 101, 102, 100, 102, 103,
+ 104, 105, 106, 104, 106, 107,
+ 108, 109, 110, 108, 110, 111,
+ 112, 113, 114, 112, 114, 115,
+ 116, 117, 118, 116, 118, 119,
+ 120, 121, 122, 120, 122, 123,
+ 124, 125, 126, 124, 126, 127
+};
+#else
+static void generateQuadIndices(int n) {
+ int index = 0;
+ for (int i = 0; i < n; i++) {
+ SkDebugf(" %3d, %3d, %3d, %3d, %3d, %3d,\n",
+ index, index + 1, index + 2, index, index + 2, index + 3);
+ index += 4;
+ }
+}
+#endif
+
+void SkGLDrawProcs::drawQuads() {
+ SkASSERT(SK_ARRAY_COUNT(gQuadIndices) == MAX_QUADS * 6);
+
+ glBindTexture(GL_TEXTURE_2D, fCurrTexture);
+
+#if 0
+ static bool gOnce;
+ if (!gOnce) {
+ generateQuadIndices(MAX_QUADS);
+ gOnce = true;
+ }
+#endif
+
+ // convert from quad vertex count to triangle vertex count
+ // 6/4 * n == n + (n >> 1) since n is always a multiple of 4
+ SkASSERT((fCurrQuad & 3) == 0);
+ int count = fCurrQuad + (fCurrQuad >> 1);
+
+ if (fClip->isComplex()) {
+ SkGLClipIter iter(fViewportHeight);
+ iter.reset(*fClip);
+ while (!iter.done()) {
+ iter.scissor();
+ glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, gQuadIndices);
+ iter.next();
+ }
+ } else {
+ glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_BYTE, gQuadIndices);
+ }
+}
+
+void SkGLDevice::setupForText(SkDraw* draw, const SkPaint& paint) {
+ // we handle complex clips in the SkDraw common code, so we don't check
+ // for it here
+ this->updateMatrixClip();
+
+ SkGL::SetPaint(paint, false);
+
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ // deferred allocation
+ if (NULL == fDrawProcs) {
+ fDrawProcs = SkNEW(SkGLDrawProcs);
+ fDrawProcs->fD1GProc = SkGL_Draw1Glyph;
+ }
+
+ // init our (and GL's) state
+ fDrawProcs->init(draw->fClip, this->height());
+ // assign to the caller's SkDraw
+ draw->fProcs = fDrawProcs;
+
+ glEnable(GL_TEXTURE_2D);
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glShadeModel(GL_FLAT);
+}
+
+void SkGLDevice::drawText(const SkDraw& draw, const void* text,
+ size_t byteLength, SkScalar x, SkScalar y,
+ const SkPaint& paint) {
+ /* Currently, perspective text is draw via paths, invoked directly by
+ SkDraw. This can't work for us, since the bitmap that our draw points
+ to has no pixels, so we just abort if we're in perspective.
+
+ Better fix would be to...
+ - have a callback inside draw to handle path drawing
+ - option to have draw call the font cache, which we could patch (?)
+ */
+ if (draw.fMatrix->getType() & SkMatrix::kPerspective_Mask) {
+ return;
+ }
+
+ SkDraw myDraw(draw);
+ this->setupForText(&myDraw, paint);
+ this->INHERITED::drawText(myDraw, text, byteLength, x, y, paint);
+ fDrawProcs->flush();
+ glPopMatrix(); // GL_MODELVIEW
+}
+
+void SkGLDevice::drawPosText(const SkDraw& draw, const void* text,
+ size_t byteLength, const SkScalar pos[],
+ SkScalar constY, int scalarsPerPos,
+ const SkPaint& paint) {
+ if (draw.fMatrix->getType() & SkMatrix::kPerspective_Mask) {
+ return;
+ }
+
+ SkDraw myDraw(draw);
+ this->setupForText(&myDraw, paint);
+ this->INHERITED::drawPosText(myDraw, text, byteLength, pos, constY,
+ scalarsPerPos, paint);
+ fDrawProcs->flush();
+ glPopMatrix(); // GL_MODELVIEW
+}
+
+void SkGLDevice::drawTextOnPath(const SkDraw& draw, const void* text,
+ size_t byteLength, const SkPath& path,
+ const SkMatrix* m, const SkPaint& paint) {
+ // not supported yet
+}
+
diff --git a/skia/gl/SkGLDevice.h b/skia/gl/SkGLDevice.h
new file mode 100644
index 0000000..9e86f3f
--- /dev/null
+++ b/skia/gl/SkGLDevice.h
@@ -0,0 +1,121 @@
+#ifndef SkGLDevice_DEFINED
+#define SkGLDevice_DEFINED
+
+#include "SkDevice.h"
+#include "SkGL.h"
+#include "SkRegion.h"
+
+struct SkGLDrawProcs;
+
+class SkGLDevice : public SkDevice {
+public:
+ SkGLDevice(const SkBitmap& bitmap, bool offscreen);
+ virtual ~SkGLDevice();
+
+ enum TexOrientation {
+ kNo_TexOrientation,
+ kTopToBottom_TexOrientation,
+ kBottomToTop_TexOrientation
+ };
+
+ /** Called when this device is no longer a candidate for a render target,
+ but will instead be used as a texture to be drawn. Be sure to call
+ the base impl if you override, as it will compute size and max.
+ */
+ virtual TexOrientation bindDeviceAsTexture();
+
+ // returns true if complex
+ SkGLClipIter* updateMatrixClip();
+ // call to set the clip to the specified rect
+ void scissor(const SkIRect&);
+
+ // overrides from SkDevice
+ virtual void gainFocus(SkCanvas*);
+ virtual void setMatrixClip(const SkMatrix& matrix, const SkRegion& clip);
+
+ virtual void drawPaint(const SkDraw&, const SkPaint& paint);
+ virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count,
+ const SkPoint[], const SkPaint& paint);
+ virtual void drawRect(const SkDraw&, const SkRect& r,
+ const SkPaint& paint);
+ virtual void drawPath(const SkDraw&, const SkPath& path,
+ const SkPaint& paint);
+ virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
+ const SkMatrix& matrix, const SkPaint& paint);
+ virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap,
+ int x, int y, const SkPaint& paint);
+ virtual void drawText(const SkDraw&, const void* text, size_t len,
+ SkScalar x, SkScalar y, const SkPaint& paint);
+ virtual void drawPosText(const SkDraw&, const void* text, size_t len,
+ const SkScalar pos[], SkScalar constY,
+ int scalarsPerPos, const SkPaint& paint);
+ virtual void drawTextOnPath(const SkDraw&, const void* text, size_t len,
+ const SkPath& path, const SkMatrix* matrix,
+ const SkPaint& paint);
+ virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCount,
+ const SkPoint verts[], const SkPoint texs[],
+ const SkColor colors[], SkXfermode* xmode,
+ const uint16_t indices[], int indexCount,
+ const SkPaint& paint);
+ virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y,
+ const SkPaint&);
+
+protected:
+ /** Return the current glmatrix, from a previous call to setMatrixClip */
+ const SkMatrix& matrix() const { return fMatrix; }
+ /** Return the current clip, from a previous call to setMatrixClip */
+ const SkRegion& clip() const { return fClip; }
+
+private:
+ SkGLMatrix fGLMatrix;
+ SkMatrix fMatrix;
+ SkRegion fClip;
+ bool fDirty;
+
+ SkGLClipIter fClipIter;
+ SkGLDrawProcs* fDrawProcs;
+
+ void setupForText(SkDraw* draw, const SkPaint& paint);
+
+ // global texture cache methods
+ class TexCache;
+ static TexCache* LockTexCache(const SkBitmap&, GLuint* name,
+ SkPoint* size);
+ static void UnlockTexCache(TexCache*);
+ class SkAutoLockTexCache {
+ public:
+ SkAutoLockTexCache(const SkBitmap& bitmap, GLuint* name,
+ SkPoint* size) {
+ fTex = SkGLDevice::LockTexCache(bitmap, name, size);
+ }
+ ~SkAutoLockTexCache() {
+ if (fTex) {
+ SkGLDevice::UnlockTexCache(fTex);
+ }
+ }
+ TexCache* get() const { return fTex; }
+ private:
+ TexCache* fTex;
+ };
+ friend class SkAutoTexCache;
+
+ // returns cache if the texture is bound for the shader
+ TexCache* setupGLPaintShader(const SkPaint& paint);
+
+ class AutoPaintShader {
+ public:
+ AutoPaintShader(SkGLDevice*, const SkPaint& paint);
+ ~AutoPaintShader();
+
+ bool useTex() const { return fTexCache != 0; }
+ private:
+ SkGLDevice* fDevice;
+ TexCache* fTexCache;
+ };
+ friend class AutoPaintShader;
+
+ typedef SkDevice INHERITED;
+};
+
+#endif
+
diff --git a/skia/gl/SkGLDevice_FBO.cpp b/skia/gl/SkGLDevice_FBO.cpp
new file mode 100644
index 0000000..552d619
--- /dev/null
+++ b/skia/gl/SkGLDevice_FBO.cpp
@@ -0,0 +1,57 @@
+#include "SkGLDevice_FBO.h"
+#include "SkRegion.h"
+
+SkGLDevice_FBO::SkGLDevice_FBO(const SkBitmap& bitmap, bool offscreen)
+ : SkGLDevice(bitmap, offscreen) {
+ fFBO = 0;
+ fTextureID = 0;
+
+ if (offscreen) {
+ int nw = SkNextPow2(bitmap.rowBytesAsPixels());
+ int nh = SkNextPow2(bitmap.height());
+
+ glGenFramebuffersEXT(1, &fFBO);
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fFBO);
+
+ glGenTextures(1, &fTextureID);
+ glBindTexture(GL_TEXTURE_2D, fTextureID);
+ SkGL::SetTexParamsClamp(false);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, nw, nh, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D, fTextureID, 0);
+ GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+ if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
+ SkDebugf("-- glCheckFramebufferStatusEXT %x\n", status);
+ }
+
+ // now reset back to "normal" drawing target
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+ }
+}
+
+SkGLDevice_FBO::~SkGLDevice_FBO() {
+ if (fTextureID) {
+ glDeleteTextures(1, &fTextureID);
+ }
+ if (fFBO) {
+ glDeleteFramebuffersEXT(1, &fFBO);
+ }
+}
+
+SkGLDevice::TexOrientation SkGLDevice_FBO::bindDeviceAsTexture() {
+ if (fTextureID) {
+ glBindTexture(GL_TEXTURE_2D, fTextureID);
+ return kBottomToTop_TexOrientation;
+ }
+ return kNo_TexOrientation;
+}
+
+void SkGLDevice_FBO::gainFocus(SkCanvas* canvas) {
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fFBO);
+
+ // now we're ready for the viewport and projection matrix
+ this->INHERITED::gainFocus(canvas);
+}
+
diff --git a/skia/gl/SkGLDevice_FBO.h b/skia/gl/SkGLDevice_FBO.h
new file mode 100644
index 0000000..d695ff0
--- /dev/null
+++ b/skia/gl/SkGLDevice_FBO.h
@@ -0,0 +1,23 @@
+#ifndef SkGLDevice_FBO_DEFINED
+#define SkGLDevice_FBO_DEFINED
+
+#include "SkGLDevice.h"
+
+class SkGLDevice_FBO : public SkGLDevice {
+public:
+ SkGLDevice_FBO(const SkBitmap& bitmap, bool offscreen);
+ virtual ~SkGLDevice_FBO();
+
+ // overrides from SkGLDevice
+ virtual void gainFocus(SkCanvas*);
+ virtual TexOrientation bindDeviceAsTexture();
+
+private:
+ GLuint fFBO;
+ GLuint fTextureID;
+
+ typedef SkGLDevice INHERITED;
+};
+
+#endif
+
diff --git a/skia/gl/SkGLDevice_SWLayer.cpp b/skia/gl/SkGLDevice_SWLayer.cpp
new file mode 100644
index 0000000..4b75d4c
--- /dev/null
+++ b/skia/gl/SkGLDevice_SWLayer.cpp
@@ -0,0 +1,91 @@
+#include "SkGLDevice_SWLayer.h"
+#include "SkRegion.h"
+
+SkGLDevice_SWLayer::SkGLDevice_SWLayer(const SkBitmap& bitmap)
+ : SkGLDevice(bitmap, true) {
+ fTextureID = 0;
+
+ SkASSERT(bitmap.getPixels());
+}
+
+SkGLDevice_SWLayer::~SkGLDevice_SWLayer() {
+ if (fTextureID) {
+ glDeleteTextures(1, &fTextureID);
+ }
+}
+
+SkGLDevice::TexOrientation SkGLDevice_SWLayer::bindDeviceAsTexture() {
+ const SkBitmap& bitmap = this->accessBitmap(false);
+
+ if (0 == fTextureID) {
+ fTextureID = SkGL::BindNewTexture(bitmap, NULL);
+ }
+ return kTopToBottom_TexOrientation;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkDraw.h"
+
+void SkGLDevice_SWLayer::drawPaint(const SkDraw& draw, const SkPaint& paint) {
+ draw.drawPaint(paint);
+}
+
+void SkGLDevice_SWLayer::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count,
+ const SkPoint pts[], const SkPaint& paint) {
+ draw.drawPoints(mode, count, pts, paint);
+}
+
+void SkGLDevice_SWLayer::drawRect(const SkDraw& draw, const SkRect& r,
+ const SkPaint& paint) {
+ draw.drawRect(r, paint);
+}
+
+void SkGLDevice_SWLayer::drawPath(const SkDraw& draw, const SkPath& path,
+ const SkPaint& paint) {
+ draw.drawPath(path, paint);
+}
+
+void SkGLDevice_SWLayer::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
+ const SkMatrix& matrix, const SkPaint& paint) {
+ draw.drawBitmap(bitmap, matrix, paint);
+}
+
+void SkGLDevice_SWLayer::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
+ int x, int y, const SkPaint& paint) {
+ draw.drawSprite(bitmap, x, y, paint);
+}
+
+void SkGLDevice_SWLayer::drawText(const SkDraw& draw, const void* text, size_t len,
+ SkScalar x, SkScalar y, const SkPaint& paint) {
+ draw.drawText((const char*)text, len, x, y, paint);
+}
+
+void SkGLDevice_SWLayer::drawPosText(const SkDraw& draw, const void* text, size_t len,
+ const SkScalar xpos[], SkScalar y,
+ int scalarsPerPos, const SkPaint& paint) {
+ draw.drawPosText((const char*)text, len, xpos, y, scalarsPerPos, paint);
+}
+
+void SkGLDevice_SWLayer::drawTextOnPath(const SkDraw& draw, const void* text,
+ size_t len, const SkPath& path,
+ const SkMatrix* matrix,
+ const SkPaint& paint) {
+ draw.drawTextOnPath((const char*)text, len, path, matrix, paint);
+}
+
+void SkGLDevice_SWLayer::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
+ int vertexCount,
+ const SkPoint verts[], const SkPoint textures[],
+ const SkColor colors[], SkXfermode* xmode,
+ const uint16_t indices[], int indexCount,
+ const SkPaint& paint) {
+ draw.drawVertices(vmode, vertexCount, verts, textures, colors, xmode,
+ indices, indexCount, paint);
+}
+
+void SkGLDevice_SWLayer::drawDevice(const SkDraw& draw, SkDevice* dev,
+ int x, int y, const SkPaint& paint) {
+ this->SkDevice::drawDevice(draw, dev, x, y, paint);
+}
+
diff --git a/skia/gl/SkGLDevice_SWLayer.h b/skia/gl/SkGLDevice_SWLayer.h
new file mode 100644
index 0000000..7e61370
--- /dev/null
+++ b/skia/gl/SkGLDevice_SWLayer.h
@@ -0,0 +1,49 @@
+#ifndef SkGLDevice_SWLayer_DEFINED
+#define SkGLDevice_SWLayer_DEFINED
+
+#include "SkGLDevice.h"
+
+class SkGLDevice_SWLayer : public SkGLDevice {
+public:
+ SkGLDevice_SWLayer(const SkBitmap& bitmap);
+ virtual ~SkGLDevice_SWLayer();
+
+ // overrides from SkGLDevice
+ virtual TexOrientation bindDeviceAsTexture();
+
+ // overrides from SkDevice
+ virtual void drawPaint(const SkDraw&, const SkPaint& paint);
+ virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count,
+ const SkPoint[], const SkPaint& paint);
+ virtual void drawRect(const SkDraw&, const SkRect& r,
+ const SkPaint& paint);
+ virtual void drawPath(const SkDraw&, const SkPath& path,
+ const SkPaint& paint);
+ virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
+ const SkMatrix& matrix, const SkPaint& paint);
+ virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap,
+ int x, int y, const SkPaint& paint);
+ virtual void drawText(const SkDraw&, const void* text, size_t len,
+ SkScalar x, SkScalar y, const SkPaint& paint);
+ virtual void drawPosText(const SkDraw&, const void* text, size_t len,
+ const SkScalar pos[], SkScalar constY,
+ int scalarsPerPos, const SkPaint& paint);
+ virtual void drawTextOnPath(const SkDraw&, const void* text, size_t len,
+ const SkPath& path, const SkMatrix* matrix,
+ const SkPaint& paint);
+ virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCount,
+ const SkPoint verts[], const SkPoint texs[],
+ const SkColor colors[], SkXfermode* xmode,
+ const uint16_t indices[], int indexCount,
+ const SkPaint& paint);
+ virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y,
+ const SkPaint&);
+
+private:
+ GLuint fTextureID;
+
+ typedef SkGLDevice INHERITED;
+};
+
+#endif
+
diff --git a/skia/gl/SkGLTextCache.cpp b/skia/gl/SkGLTextCache.cpp
new file mode 100644
index 0000000..25f822e
--- /dev/null
+++ b/skia/gl/SkGLTextCache.cpp
@@ -0,0 +1,201 @@
+#include "SkGLTextCache.h"
+#include "SkScalerContext.h"
+#include "SkTSearch.h"
+
+const GLenum gTextTextureFormat = GL_ALPHA;
+const GLenum gTextTextureType = GL_UNSIGNED_BYTE;
+
+SkGLTextCache::Strike::Strike(Strike* next, int width, int height) {
+ fStrikeWidth = SkNextPow2(SkMax32(kMinStrikeWidth, width));
+ fStrikeHeight = SkNextPow2(height);
+ fGlyphCount = 0;
+ fNextFreeOffsetX = 0;
+ fNext = next;
+
+ fStrikeWidthShift = SkNextLog2(fStrikeWidth);
+ fStrikeHeightShift = SkNextLog2(fStrikeHeight);
+
+ if (next) {
+ SkASSERT(next->fStrikeHeight == fStrikeHeight);
+ }
+
+ // create an empty texture to receive glyphs
+ fTexName = 0;
+ glGenTextures(1, &fTexName);
+ glBindTexture(GL_TEXTURE_2D, fTexName);
+ glTexImage2D(GL_TEXTURE_2D, 0, gTextTextureFormat,
+ fStrikeWidth, fStrikeHeight, 0,
+ gTextTextureFormat, gTextTextureType, NULL);
+
+ SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ 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);
+}
+
+SkGLTextCache::Strike::~Strike() {
+ if (fTexName != 0) {
+ glDeleteTextures(1, &fTexName);
+ }
+}
+
+SkGLTextCache::Strike*
+SkGLTextCache::Strike::findGlyph(const SkGlyph& glyph, int* offset) {
+ Strike* strike = this;
+ SkDEBUGCODE(const int height = SkNextPow2(glyph.fHeight);)
+
+ do {
+ SkASSERT(height == strike->fStrikeHeight);
+
+ int index = SkTSearch(strike->fGlyphIDArray, strike->fGlyphCount,
+ glyph.fID, sizeof(strike->fGlyphIDArray[0]));
+ if (index >= 0) {
+ if (offset) {
+ *offset = strike->fGlyphOffsetX[index];
+ }
+ return strike;
+ }
+ strike = strike->fNext;
+ } while (NULL != strike);
+ return NULL;
+}
+
+static void make_a_whole(void* buffer, int index, int count, size_t elemSize) {
+ SkASSERT(index >= 0 && index <= count);
+ size_t offset = index * elemSize;
+ memmove((char*)buffer + offset + elemSize,
+ (const char*)buffer + offset,
+ (count - index) * elemSize);
+}
+
+SkGLTextCache::Strike*
+SkGLTextCache::Strike::addGlyphAndBind(const SkGlyph& glyph,
+ const uint8_t image[], int* offset) {
+#ifdef SK_DEBUG
+ SkASSERT(this->findGlyph(glyph, NULL) == NULL);
+ const int height = SkNextPow2(glyph.fHeight);
+ SkASSERT(height <= fStrikeHeight && height > (fStrikeHeight >> 1));
+#endif
+
+ int rowBytes = glyph.rowBytes();
+ SkASSERT(rowBytes >= glyph.fWidth);
+
+ Strike* strike;
+ if (fGlyphCount == kMaxGlyphCount ||
+ fNextFreeOffsetX + rowBytes >= fStrikeWidth) {
+ // this will bind the next texture for us
+ SkDebugf("--- extend strike %p\n", this);
+ strike = SkNEW_ARGS(Strike, (this, rowBytes, glyph.fHeight));
+ } else {
+ glBindTexture(GL_TEXTURE_2D, fTexName);
+ strike = this;
+ }
+
+ uint32_t* idArray = strike->fGlyphIDArray;
+ uint16_t* offsetArray = strike->fGlyphOffsetX;
+ const int glyphCount = strike->fGlyphCount;
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, strike->fNextFreeOffsetX, 0, rowBytes,
+ glyph.fHeight, gTextTextureFormat, gTextTextureType,
+ image);
+
+ // need to insert the offset
+ int index = SkTSearch(idArray, glyphCount, glyph.fID, sizeof(idArray[0]));
+ SkASSERT(index < 0);
+ index = ~index; // this is where we should insert it
+ make_a_whole(idArray, index, glyphCount, sizeof(idArray));
+ make_a_whole(offsetArray, index, glyphCount, sizeof(offsetArray[0]));
+ idArray[index] = glyph.fID;
+ offsetArray[index] = strike->fNextFreeOffsetX;
+ if (offset) {
+ *offset = strike->fNextFreeOffsetX;
+ }
+
+#if 0
+ SkDebugf("--- strike %p glyph %x [%d %d] offset %d count %d\n",
+ strike, glyph.fID, glyph.fWidth, glyph.fHeight,
+ strike->fNextFreeOffsetX, glyphCount + 1);
+#endif
+
+ // now update our header
+ strike->fGlyphCount = glyphCount + 1;
+ strike->fNextFreeOffsetX += glyph.fWidth;
+ return strike;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGLTextCache::SkGLTextCache() {
+ fCtx = SkGetGLContext();
+ bzero(fStrikeList, sizeof(fStrikeList));
+}
+
+SkGLTextCache::~SkGLTextCache() {
+ SkDebugf("--- delete textcache %p\n", this);
+
+ // if true, we need to not call glDeleteTexture, since they will have
+ // already gone out of scope
+ bool zap = SkGetGLContext() != fCtx;
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(fStrikeList); i++) {
+ Strike* strike = fStrikeList[i];
+ while (strike != NULL) {
+ Strike* next = strike->fNext;
+ if (zap) {
+ strike->zapTexture();
+ }
+ SkDELETE(strike);
+ strike = next;
+ }
+ }
+}
+
+SkGLTextCache::Strike* SkGLTextCache::findGlyph(const SkGlyph& glyph,
+ int* offset) {
+ if (SkGetGLContext() != fCtx) {
+ SkDebugf("====== stale context for text texture\n");
+ }
+
+ SkASSERT(glyph.fWidth != 0);
+ SkASSERT(glyph.fHeight != 0);
+
+ size_t index = SkNextLog2(glyph.fHeight);
+ if (index >= SK_ARRAY_COUNT(fStrikeList)) {
+ // too big for us to cache;
+ return NULL;
+ }
+
+ Strike* strike = fStrikeList[index];
+ if (strike) {
+ strike = strike->findGlyph(glyph, offset);
+ }
+ return strike;
+}
+
+SkGLTextCache::Strike* SkGLTextCache::addGlyphAndBind(const SkGlyph& glyph,
+ const uint8_t image[], int* offset) {
+ if (SkGetGLContext() != fCtx) {
+ SkDebugf("====== stale context for text texture\n");
+ }
+
+ SkASSERT(image != NULL);
+ SkASSERT(glyph.fWidth != 0);
+ SkASSERT(glyph.fHeight != 0);
+
+ size_t index = SkNextLog2(glyph.fHeight);
+ if (index >= SK_ARRAY_COUNT(fStrikeList)) {
+ // too big for us to cache;
+ return NULL;
+ }
+
+ Strike* strike = fStrikeList[index];
+ if (NULL == strike) {
+ strike = SkNEW_ARGS(Strike, (NULL, glyph.rowBytes(), glyph.fHeight));
+// SkDebugf("--- create strike [%d] %p cache %p\n", index, strike, this);
+ }
+ strike = strike->addGlyphAndBind(glyph, image, offset);
+ fStrikeList[index] = strike;
+ return strike;
+}
+
diff --git a/skia/gl/SkGLTextCache.h b/skia/gl/SkGLTextCache.h
new file mode 100644
index 0000000..386b274
--- /dev/null
+++ b/skia/gl/SkGLTextCache.h
@@ -0,0 +1,83 @@
+#ifndef SkGLTextCache_DEFINED
+#define SkGLTextCache_DEFINED
+
+#include "SkGL.h"
+
+class SkGlyph;
+
+class SkGLTextCache {
+public:
+ SkGLTextCache();
+ ~SkGLTextCache();
+
+ void* getCtx() const { return fCtx; }
+
+ class Strike {
+ public:
+ int width() const { return fStrikeWidth; }
+ int height() const { return fStrikeHeight; }
+ GLuint texture() const { return fTexName; }
+ int widthShift() const { return fStrikeWidthShift; }
+ int heightShift() const { return fStrikeHeightShift; }
+
+ // call this to force us to ignore the texture name in our destructor
+ // only call it right before our destructor
+ void zapTexture() { fTexName = 0; }
+
+ private:
+ // if next is non-null, its height must match our height
+ Strike(Strike* next, int width, int height);
+ ~Strike();
+
+ Strike* findGlyph(const SkGlyph&, int* offset);
+ Strike* addGlyphAndBind(const SkGlyph&, const uint8_t*, int* offset);
+
+ enum {
+ kMinStrikeWidth = 1024,
+ kMaxGlyphCount = 256
+ };
+
+ Strike* fNext;
+ GLuint fTexName;
+ uint32_t fGlyphIDArray[kMaxGlyphCount]; // stores glyphIDs
+ uint16_t fGlyphOffsetX[kMaxGlyphCount]; // stores x-offsets
+ uint16_t fGlyphCount;
+ uint16_t fNextFreeOffsetX;
+ uint16_t fStrikeWidth;
+ uint16_t fStrikeHeight;
+ uint8_t fStrikeWidthShift; // pow2(fStrikeWidth)
+ uint8_t fStrikeHeightShift; // pow2(fStrikeHeight)
+
+ friend class SkGLTextCache;
+ };
+
+ /** If found, returns the exact strike containing it (there may be more than
+ one with a given height), and sets offset to the offset for that glyph
+ (if not null). Does NOT bind the texture.
+ If not found, returns null and ignores offset param.
+ */
+ Strike* findGlyph(const SkGlyph&, int* offset);
+
+ /** Adds the specified glyph to this list of strikes, returning the new
+ head of the list. If offset is not null, it is set to the offset
+ for this glyph within the strike. The associated texture is bound
+ to the gl context.
+ */
+ Strike* addGlyphAndBind(const SkGlyph&, const uint8_t image[], int* offset);
+
+private:
+ void* fCtx;
+
+ enum {
+ // greater than this we won't cache
+ kMaxGlyphHeightShift = 9,
+
+ kMaxGlyphHeight = 1 << kMaxGlyphHeightShift,
+ kMaxStrikeListCount = kMaxGlyphHeightShift + 1
+ };
+
+ // heads of the N families, one for each pow2 height
+ Strike* fStrikeList[kMaxStrikeListCount];
+};
+
+#endif
diff --git a/skia/gl/SkTextureCache.cpp b/skia/gl/SkTextureCache.cpp
new file mode 100644
index 0000000..6d578fe
--- /dev/null
+++ b/skia/gl/SkTextureCache.cpp
@@ -0,0 +1,362 @@
+#include "SkTextureCache.h"
+
+//#define TRACE_HASH_HITS
+
+SkTextureCache::Entry::Entry(const SkBitmap& bitmap)
+ : fName(0), fKey(bitmap), fPrev(NULL), fNext(NULL) {
+
+ fMemSize = SkGL::ComputeTextureMemorySize(bitmap);
+ fLockCount = 0;
+}
+
+SkTextureCache::Entry::~Entry() {
+ if (fName != 0) {
+ glDeleteTextures(1, &fName);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkTextureCache::SkTextureCache(size_t countMax, size_t sizeMax)
+ : fHead(NULL), fTail(NULL),
+ fTexCountMax(countMax), fTexSizeMax(sizeMax),
+ fTexCount(0), fTexSize(0) {
+
+ bzero(fHash, sizeof(fHash));
+ this->validate();
+}
+
+SkTextureCache::~SkTextureCache() {
+#ifdef SK_DEBUG
+ Entry* entry = fHead;
+ while (entry) {
+ SkASSERT(entry->lockCount() == 0);
+ entry = entry->fNext;
+ }
+#endif
+ this->validate();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int SkTextureCache::findInSorted(const Key& key) const {
+ int count = fSorted.count();
+ if (count == 0) {
+ return ~0;
+ }
+
+ Entry** sorted = fSorted.begin();
+ int lo = 0;
+ int hi = count - 1;
+ while (lo < hi) {
+ int mid = (hi + lo) >> 1;
+ if (sorted[mid]->getKey() < key) {
+ lo = mid + 1;
+ } else {
+ hi = mid;
+ }
+ }
+
+ // hi is now our best guess
+ const Entry* entry = sorted[hi];
+ if (entry->getKey() == key) {
+ return hi;
+ }
+
+ // return where to insert it
+ if (entry->getKey() < key) {
+ hi += 1;
+ }
+ return ~hi; // we twiddle to indicate not-found
+}
+
+#ifdef TRACE_HASH_HITS
+static int gHashHits;
+static int gSortedHits;
+#endif
+
+SkTextureCache::Entry* SkTextureCache::find(const Key& key, int* insert) const {
+ int count = fSorted.count();
+ if (count == 0) {
+ *insert = 0;
+ return NULL;
+ }
+
+ // check the hash first
+ int hashIndex = key.getHashIndex();
+ Entry* entry = fHash[hashIndex];
+ if (NULL != entry && entry->getKey() == key) {
+#ifdef TRACE_HASH_HITS
+ gHashHits += 1;
+#endif
+ return entry;
+ }
+
+ int index = this->findInSorted(key);
+ if (index >= 0) {
+#ifdef TRACE_HASH_HITS
+ gSortedHits += 1;
+#endif
+ entry = fSorted[index];
+ fHash[hashIndex] = entry;
+ return entry;
+ }
+
+ // ~index is where to insert the entry
+ *insert = ~index;
+ return NULL;
+}
+
+SkTextureCache::Entry* SkTextureCache::lock(const SkBitmap& bitmap) {
+ this->validate();
+
+ // call this before we call find(), so we don't reorder after find() and
+ // invalidate our index
+ this->purgeIfNecessary(SkGL::ComputeTextureMemorySize(bitmap));
+
+ Key key(bitmap);
+ int index;
+ Entry* entry = this->find(key, &index);
+
+ if (NULL == entry) {
+ entry = SkNEW_ARGS(Entry, (bitmap));
+
+ entry->fName = SkGL::BindNewTexture(bitmap, &entry->fTexSize);
+ if (0 == entry->fName) {
+ SkDELETE(entry);
+ return NULL;
+ }
+ fHash[key.getHashIndex()] = entry;
+ *fSorted.insert(index) = entry;
+
+ fTexCount += 1;
+ fTexSize += entry->memSize();
+ } else {
+ // detach from our llist
+ Entry* prev = entry->fPrev;
+ Entry* next = entry->fNext;
+ if (prev) {
+ prev->fNext = next;
+ } else {
+ SkASSERT(fHead == entry);
+ fHead = next;
+ }
+ if (next) {
+ next->fPrev = prev;
+ } else {
+ SkASSERT(fTail == entry);
+ fTail = prev;
+ }
+ // now bind the texture
+ glBindTexture(GL_TEXTURE_2D, entry->fName);
+ }
+
+ // add to head of llist for LRU
+ entry->fPrev = NULL;
+ entry->fNext = fHead;
+ if (NULL != fHead) {
+ SkASSERT(NULL == fHead->fPrev);
+ fHead->fPrev = entry;
+ }
+ fHead = entry;
+ if (NULL == fTail) {
+ fTail = entry;
+ }
+
+ this->validate();
+ entry->lock();
+
+#ifdef TRACE_HASH_HITS
+ SkDebugf("---- texture cache hash=%d sorted=%d\n", gHashHits, gSortedHits);
+#endif
+ return entry;
+}
+
+void SkTextureCache::unlock(Entry* entry) {
+ this->validate();
+
+#ifdef SK_DEBUG
+ SkASSERT(entry);
+ int index = this->findInSorted(entry->getKey());
+ SkASSERT(fSorted[index] == entry);
+#endif
+
+ SkASSERT(entry->fLockCount > 0);
+ entry->unlock();
+}
+
+void SkTextureCache::purgeIfNecessary(size_t extraSize) {
+ this->validate();
+
+ size_t countMax = fTexCountMax;
+ size_t sizeMax = fTexSizeMax;
+
+ // take extraSize into account, but watch for underflow of size_t
+ if (extraSize > sizeMax) {
+ sizeMax = 0;
+ } else {
+ sizeMax -= extraSize;
+ }
+
+ Entry* entry = fTail;
+ while (entry) {
+ if (fTexCount <= countMax && fTexSize <= sizeMax) {
+ break;
+ }
+
+ Entry* prev = entry->fPrev;
+ // don't purge an entry that is locked
+ if (entry->isLocked()) {
+ entry = prev;
+ continue;
+ }
+
+ fTexCount -= 1;
+ fTexSize -= entry->memSize();
+
+ // remove from our sorted and hash arrays
+ int index = this->findInSorted(entry->getKey());
+ SkASSERT(index >= 0);
+ fSorted.remove(index);
+ index = entry->getKey().getHashIndex();
+ if (entry == fHash[index]) {
+ fHash[index] = NULL;
+ }
+
+ // now detach it from our llist
+ Entry* next = entry->fNext;
+ if (prev) {
+ prev->fNext = next;
+ } else {
+ fHead = next;
+ }
+ if (next) {
+ next->fPrev = prev;
+ } else {
+ fTail = prev;
+ }
+
+ // now delete it
+ SkDebugf("---- purge texture cache %d size=%d\n",
+ entry->name(), entry->memSize());
+ SkDELETE(entry);
+
+ // keep going
+ entry = prev;
+ }
+
+ this->validate();
+}
+
+void SkTextureCache::setMaxCount(size_t count) {
+ if (fTexCountMax != count) {
+ fTexCountMax = count;
+ this->purgeIfNecessary(0);
+ }
+}
+
+void SkTextureCache::setMaxSize(size_t size) {
+ if (fTexSizeMax != size) {
+ fTexSizeMax = size;
+ this->purgeIfNecessary(0);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkTextureCache::zapAllTextures() {
+ SkDebugf("---- zapAllTextures\n");
+
+ this->validate();
+
+ Entry* entry = fHead;
+ while (entry) {
+ Entry* next = entry->fNext;
+ entry->zapName();
+ SkDELETE(entry);
+ entry = next;
+ }
+
+ fSorted.reset();
+ bzero(fHash, sizeof(fHash));
+
+ fTexCount = 0;
+ fTexSize = 0;
+
+ fTail = fHead = NULL;
+
+ this->validate();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+void SkTextureCache::validate() const {
+ if (0 == fTexCount) {
+ SkASSERT(0 == fTexSize);
+ SkASSERT(NULL == fHead);
+ SkASSERT(NULL == fTail);
+ return;
+ }
+
+ SkASSERT(fTexSize); // do we allow a zero-sized texture?
+ SkASSERT(fHead);
+ SkASSERT(fTail);
+
+ SkASSERT(NULL == fHead->fPrev);
+ SkASSERT(NULL == fTail->fNext);
+ if (1 == fTexCount) {
+ SkASSERT(fHead == fTail);
+ }
+
+ const Entry* entry = fHead;
+ size_t count = 0;
+ size_t size = 0;
+ size_t i;
+
+ while (entry != NULL) {
+ SkASSERT(count < fTexCount);
+ SkASSERT(size < fTexSize);
+ size += entry->memSize();
+ count += 1;
+ if (NULL == entry->fNext) {
+ SkASSERT(fTail == entry);
+ }
+ entry = entry->fNext;
+ }
+ SkASSERT(count == fTexCount);
+ SkASSERT(size == fTexSize);
+
+ count = 0;
+ size = 0;
+ entry = fTail;
+ while (entry != NULL) {
+ SkASSERT(count < fTexCount);
+ SkASSERT(size < fTexSize);
+ size += entry->memSize();
+ count += 1;
+ if (NULL == entry->fPrev) {
+ SkASSERT(fHead == entry);
+ }
+ entry = entry->fPrev;
+ }
+ SkASSERT(count == fTexCount);
+ SkASSERT(size == fTexSize);
+
+ SkASSERT(count == (size_t)fSorted.count());
+ for (i = 1; i < count; i++) {
+ SkASSERT(fSorted[i-1]->getKey() < fSorted[i]->getKey());
+ }
+
+ for (i = 0; i < kHashCount; i++) {
+ if (fHash[i]) {
+ size_t index = fHash[i]->getKey().getHashIndex();
+ SkASSERT(index == i);
+ index = fSorted.find(fHash[i]);
+ SkASSERT((size_t)index < count);
+ }
+ }
+}
+#endif
+
+
diff --git a/skia/gl/SkTextureCache.h b/skia/gl/SkTextureCache.h
new file mode 100644
index 0000000..e4ffc78
--- /dev/null
+++ b/skia/gl/SkTextureCache.h
@@ -0,0 +1,160 @@
+#ifndef SkTextureCache_DEFINED
+#define SkTextureCache_DEFINED
+
+#include "SkBitmap.h"
+#include "SkPoint.h"
+#include "SkGL.h"
+#include "SkTDArray.h"
+
+class SkTextureCache {
+public:
+ SkTextureCache(size_t maxCount, size_t maxSize);
+ ~SkTextureCache();
+
+ size_t getMaxCount() { return fTexCountMax; }
+ size_t getMaxSize() { return fTexSizeMax; }
+
+ void setMaxCount(size_t count);
+ void setMaxSize(size_t size);
+
+ /** Call this if the context has changed behind our backs, and the cache
+ needs to abandon all of its existing textures. This saves us from using
+ or deleting a texture created from a different context
+ */
+ void zapAllTextures();
+
+ static int HashMask() { return kHashMask; }
+
+ class Key {
+ public:
+ Key(const SkBitmap& bm) {
+ fGenID = bm.getGenerationID();
+ fOffset = bm.pixelRefOffset();
+ fWH = (bm.width() << 16) | bm.height();
+ this->computeHash();
+ }
+
+ int getHashIndex() const { return fHashIndex; }
+
+ friend bool operator==(const Key& a, const Key& b) {
+ return a.fHash == b.fHash &&
+ a.fGenID == b.fGenID &&
+ a.fOffset == b.fOffset &&
+ a.fWH == b.fWH;
+ }
+
+ friend bool operator<(const Key& a, const Key& b) {
+ if (a.fHash < b.fHash) {
+ return true;
+ } else if (a.fHash > b.fHash) {
+ return false;
+ }
+
+ if (a.fGenID < b.fGenID) {
+ return true;
+ } else if (a.fGenID > b.fGenID) {
+ return false;
+ }
+
+ if (a.fOffset < b.fOffset) {
+ return true;
+ } else if (a.fOffset > b.fOffset) {
+ return false;
+ }
+
+ return a.fWH < b.fWH;
+ }
+
+ private:
+ void computeHash() {
+ uint32_t hash = fGenID ^ fOffset ^ fWH;
+ fHash = hash;
+ hash ^= hash >> 16;
+ fHashIndex = hash & SkTextureCache::HashMask();
+ }
+
+ uint32_t fHash; // computed from the other fields
+ uint32_t fGenID;
+ size_t fOffset;
+ uint32_t fWH;
+ // for indexing into the texturecache's fHash
+ int fHashIndex;
+ };
+
+ class Entry {
+ public:
+ GLuint name() const { return fName; }
+ SkPoint texSize() const { return fTexSize; }
+ size_t memSize() const { return fMemSize; }
+ const Key& getKey() const { return fKey; }
+
+ // call this to clear the texture name, in case the context has changed
+ // in which case we should't reference or delete this texture in GL
+ void zapName() { fName = 0; }
+
+ private:
+ Entry(const SkBitmap& bitmap);
+ ~Entry();
+
+ int lockCount() const { return fLockCount; }
+ bool isLocked() const { return fLockCount > 0; }
+
+ void lock() { fLockCount += 1; }
+ void unlock() {
+ SkASSERT(fLockCount > 0);
+ fLockCount -= 1;
+ }
+
+ private:
+ GLuint fName;
+ SkPoint fTexSize;
+ Key fKey;
+ size_t fMemSize;
+ int fLockCount;
+
+ Entry* fPrev;
+ Entry* fNext;
+
+ friend class SkTextureCache;
+ };
+
+ Entry* lock(const SkBitmap&);
+ void unlock(Entry*);
+
+private:
+ void purgeIfNecessary(size_t extraSize);
+
+#ifdef SK_DEBUG
+ void validate() const;
+#else
+ void validate() const {}
+#endif
+
+ Entry* fHead;
+ Entry* fTail;
+
+ // limits for the cache
+ size_t fTexCountMax;
+ size_t fTexSizeMax;
+
+ // current values for the cache
+ size_t fTexCount;
+ size_t fTexSize;
+
+ enum {
+ kHashBits = 6,
+ kHashCount = 1 << kHashBits,
+ kHashMask = kHashCount - 1
+ };
+ mutable Entry* fHash[kHashCount];
+ SkTDArray<Entry*> fSorted;
+
+ /* If we find the key, return the entry and ignore index. If we don't,
+ return NULL and set index to the place to insert the entry in fSorted
+ */
+ Entry* find(const Key&, int* index) const;
+ // returns index or <0 if not found. Does NOT update hash
+ int findInSorted(const Key& key) const;
+};
+
+#endif