зеркало из https://github.com/mozilla/moz-skia.git
954 строки
29 KiB
C++
954 строки
29 KiB
C++
#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"
|
|
|
|
static SkMutex 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
|
|
));
|
|
}
|
|
|