1771cbf43d
This patch removes static initializers related to static and global mutexes from the final library's machine code when building on a pthread-capable system. We use PTHREAD_MUTEX_INITIALIZER to perform POD-style initialization. You need a line like the following to declare a global mutex with it: SkBaseMutex gMutex = { PTHREAD_MUTEX_INITIALIZER }; We introduce the SK_DECLARE_STATIC_MUTEX and SK_DECLARE_GLOBAL_MUTEX macros to be able to declare static/global mutexes in the source tree uniformly. SkMutex is now defined as a sub-class of SkBaseMutex, with standard construction/destruction semantics. This is useful if the mutex object is a member of another C++ class, or allocated dynamically. We also modify a few places to refer to SkBaseMutex instead of a SkMutex, where it makes sense. Generally speaking, client code should hold and use pointers to SkBaseMutex whenever they can now. We defined a new built-time macro named SK_USE_POSIX_THREADS to indicate that we're using a pthread-based SkThread.h interface. The macro will also be used in future patches to implement other helper thread synchronization classes. Finally, we inline the acquire() and release() functions in the case of Posix to improve performance a bit. Running: 'bench -repeat 10 -match mutex' on an Android device or a 2.4GHz Xeon Linux desktop shows the following improvements: Before After Galaxy Nexus 1.64 1.45 Nexus S 1.47 1.16 Xoom 1.86 1.66 Xeon 0.36 0.31 This removes 5 static mutex initializers from the library Review URL: https://codereview.appspot.com/5501066 git-svn-id: http://skia.googlecode.com/svn/trunk@3091 2bbb7eff-a529-9590-31e7-b0007b416f81
961 lines
29 KiB
C++
961 lines
29 KiB
C++
|
|
/*
|
|
* Copyright 2011 Google Inc.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
#include "SkGLDevice.h"
|
|
#include "SkGL.h"
|
|
#include "SkDrawProcs.h"
|
|
#include "SkRegion.h"
|
|
#include "SkThread.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)
|
|
|
|
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);
|
|
}
|
|
|
|
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;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
SkDevice* SkGLDeviceFactory::newDevice(SkBitmap::Config 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
|
|
}
|
|
|
|
SkGLDevice::SkGLDevice(const SkBitmap& bitmap, bool offscreen)
|
|
: SkDevice(bitmap), fClipIter(bitmap.height()) {
|
|
glEnable(GL_TEXTURE_2D);
|
|
glEnable(GL_SCISSOR_TEST);
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
|
|
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)) {
|
|
SkGL_unimpl("shader->asABitmap() == false");
|
|
return NULL;
|
|
}
|
|
|
|
bitmap.lockPixels();
|
|
if (!bitmap.readyToDraw()) {
|
|
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());
|
|
}
|
|
|
|
// must be in SkCanvas::PointMode order
|
|
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());
|
|
}
|
|
|
|
/* create a triangle strip that strokes the specified triangle. There are 8
|
|
unique vertices, but we repreat the last 2 to close up. Alternatively we
|
|
could use an indices array, and then only send 8 verts, but not sure that
|
|
would be faster.
|
|
*/
|
|
static void setStrokeRectStrip(SkGLVertex verts[10], const SkRect& rect,
|
|
SkScalar width) {
|
|
const SkScalar rad = SkScalarHalf(width);
|
|
|
|
verts[0].setScalars(rect.fLeft + rad, rect.fTop + rad);
|
|
verts[1].setScalars(rect.fLeft - rad, rect.fTop - rad);
|
|
verts[2].setScalars(rect.fRight - rad, rect.fTop + rad);
|
|
verts[3].setScalars(rect.fRight + rad, rect.fTop - rad);
|
|
verts[4].setScalars(rect.fRight - rad, rect.fBottom - rad);
|
|
verts[5].setScalars(rect.fRight + rad, rect.fBottom + rad);
|
|
verts[6].setScalars(rect.fLeft + rad, rect.fBottom - rad);
|
|
verts[7].setScalars(rect.fLeft - rad, rect.fBottom + rad);
|
|
verts[8] = verts[0];
|
|
verts[9] = verts[1];
|
|
}
|
|
|
|
void SkGLDevice::drawRect(const SkDraw& draw, const SkRect& rect,
|
|
const SkPaint& paint) {
|
|
TRACE_DRAW("coreDrawRect", this, draw);
|
|
|
|
bool doStroke = paint.getStyle() == SkPaint::kStroke_Style;
|
|
|
|
if (doStroke) {
|
|
if (paint.getStrokeJoin() != SkPaint::kMiter_Join) {
|
|
SkGL_unimpl("non-miter stroke rect");
|
|
return;
|
|
}
|
|
} else if (paint.getStrokeJoin() != SkPaint::kMiter_Join) {
|
|
SkPath path;
|
|
path.addRect(rect);
|
|
this->drawPath(draw, path, paint);
|
|
return;
|
|
}
|
|
|
|
AutoPaintShader shader(this, paint);
|
|
SkScalar width = paint.getStrokeWidth();
|
|
SkGLVertex vertex[10]; // max needed for all cases
|
|
int vertCount;
|
|
GLenum vertMode;
|
|
|
|
if (doStroke) {
|
|
if (width > 0) {
|
|
vertCount = 10;
|
|
vertMode = GL_TRIANGLE_STRIP;
|
|
setStrokeRectStrip(vertex, rect, width);
|
|
} else { // hairline
|
|
vertCount = 5;
|
|
vertMode = GL_LINE_STRIP;
|
|
vertex[0].setScalars(rect.fLeft, rect.fTop);
|
|
vertex[1].setScalars(rect.fRight, rect.fTop);
|
|
vertex[2].setScalars(rect.fRight, rect.fBottom);
|
|
vertex[3].setScalars(rect.fLeft, rect.fBottom);
|
|
vertex[4].setScalars(rect.fLeft, rect.fTop);
|
|
glLineWidth(1);
|
|
}
|
|
} else {
|
|
vertCount = 4;
|
|
vertMode = GL_TRIANGLE_FAN;
|
|
vertex->setRectFan(rect);
|
|
}
|
|
|
|
const SkGLVertex* texs = shader.useTex() ? vertex : NULL;
|
|
SkGL::DrawVertices(vertCount, vertMode, 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) {
|
|
SkGL_unimpl("stroke path");
|
|
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.readyToDraw()) {
|
|
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.readyToDraw()) {
|
|
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"
|
|
|
|
void SkGLDevice::GlyphCacheAuxProc(void* data) {
|
|
SkDebugf("-------------- delete text texture cache\n");
|
|
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(SkGLDevice::GlyphCacheAuxProc, &auxData)) {
|
|
textCache = (SkGLTextCache*)auxData;
|
|
}
|
|
if (NULL == textCache) {
|
|
// need to create one
|
|
textCache = SkNEW(SkGLTextCache);
|
|
gcache->setAuxProc(SkGLDevice::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) {
|
|
SkGL_unimpl("addGlyphAndBind failed, too big");
|
|
// 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) {
|
|
SkGL_unimpl("drawText in perspective");
|
|
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) {
|
|
SkGL_unimpl("drawPosText in perspective");
|
|
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) {
|
|
SkGL_unimpl("drawTextOnPath");
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "SkTextureCache.h"
|
|
#include "SkThread.h"
|
|
|
|
SK_DECLARE_STATIC_MUTEX(gTextureCacheMutex);
|
|
static SkTextureCache gTextureCache(kTexCountMax_Default, kTexSizeMax_Default);
|
|
|
|
SkGLDevice::TexCache* SkGLDevice::LockTexCache(const SkBitmap& bitmap,
|
|
GLuint* name, SkPoint* size) {
|
|
SkAutoMutexAcquire amc(gTextureCacheMutex);
|
|
|
|
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 SkGLDevice::GetTextureCacheMaxCount() {
|
|
SkAutoMutexAcquire amc(gTextureCacheMutex);
|
|
return gTextureCache.getMaxCount();
|
|
}
|
|
|
|
size_t SkGLDevice::GetTextureCacheMaxSize() {
|
|
SkAutoMutexAcquire amc(gTextureCacheMutex);
|
|
return gTextureCache.getMaxSize();
|
|
}
|
|
|
|
void SkGLDevice::SetTextureCacheMaxCount(size_t count) {
|
|
SkAutoMutexAcquire amc(gTextureCacheMutex);
|
|
gTextureCache.setMaxCount(count);
|
|
}
|
|
|
|
void SkGLDevice::SetTextureCacheMaxSize(size_t size) {
|
|
SkAutoMutexAcquire amc(gTextureCacheMutex);
|
|
gTextureCache.setMaxSize(size);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "SkGLTextCache.h"
|
|
|
|
static bool deleteCachesProc(SkGlyphCache* cache, void* texturesAreValid) {
|
|
void* auxData;
|
|
if (cache->getAuxProcData(SkGLDevice::GlyphCacheAuxProc, &auxData)) {
|
|
bool valid = texturesAreValid != NULL;
|
|
SkGLTextCache* textCache = static_cast<SkGLTextCache*>(auxData);
|
|
// call this before delete, in case valid is false
|
|
textCache->deleteAllStrikes(valid);
|
|
// now free the memory for the cache itself
|
|
SkDELETE(textCache);
|
|
// now remove the entry in the glyphcache (does not call the proc)
|
|
cache->removeAuxProc(SkGLDevice::GlyphCacheAuxProc);
|
|
}
|
|
return false; // keep going
|
|
}
|
|
|
|
void SkGLDevice::DeleteAllTextures() {
|
|
// free the textures in our cache
|
|
|
|
gTextureCacheMutex.acquire();
|
|
gTextureCache.deleteAllCaches(true);
|
|
gTextureCacheMutex.release();
|
|
|
|
// now free the textures in the font cache
|
|
|
|
SkGlyphCache::VisitAllCaches(deleteCachesProc, reinterpret_cast<void*>(true)
|
|
);
|
|
}
|
|
|
|
void SkGLDevice::AbandonAllTextures() {
|
|
// abandon the textures in our cache
|
|
|
|
gTextureCacheMutex.acquire();
|
|
gTextureCache.deleteAllCaches(false);
|
|
gTextureCacheMutex.release();
|
|
|
|
// abandon the textures in the font cache
|
|
|
|
SkGlyphCache::VisitAllCaches(deleteCachesProc, reinterpret_cast<void*>(false
|
|
));
|
|
}
|
|
|