aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/SkGpuDevice.cpp
diff options
context:
space:
mode:
authorGravatar reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2010-12-23 15:00:45 +0000
committerGravatar reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2010-12-23 15:00:45 +0000
commit873cb1e23b64e4b2d11749352b626bcef204bdd7 (patch)
tree16749ff8667f669deb28a00bbb5a7ce6d8db8f0d /src/gpu/SkGpuDevice.cpp
parent5b9cfd4d8809d6d55aaf047a6a8acc2de2f2eeb0 (diff)
add gpu to the default makefile
move skia-gpu files into skia/src/gpu git-svn-id: http://skia.googlecode.com/svn/trunk@653 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src/gpu/SkGpuDevice.cpp')
-rw-r--r--src/gpu/SkGpuDevice.cpp1048
1 files changed, 1048 insertions, 0 deletions
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
new file mode 100644
index 0000000000..832fc6ea75
--- /dev/null
+++ b/src/gpu/SkGpuDevice.cpp
@@ -0,0 +1,1048 @@
+/*
+ Copyright 2010 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+
+#include "GrContext.h"
+#include "GrTextContext.h"
+
+#include "SkGpuCanvas.h"
+#include "SkGpuDevice.h"
+#include "SkGrTexturePixelRef.h"
+
+#include "SkDrawProcs.h"
+#include "SkGlyphCache.h"
+
+#define CACHE_LAYER_TEXTURES 1
+
+#if 0
+ extern bool (*gShouldDrawProc)();
+ #define CHECK_SHOULD_DRAW(draw) \
+ do { \
+ if (gShouldDrawProc && !gShouldDrawProc()) return; \
+ this->prepareRenderTarget(draw); \
+ } while (0)
+#else
+ #define CHECK_SHOULD_DRAW(draw) this->prepareRenderTarget(draw)
+#endif
+
+class SkAutoExtMatrix {
+public:
+ SkAutoExtMatrix(const SkMatrix* extMatrix) {
+ if (extMatrix) {
+ SkGr::SkMatrix2GrMatrix(*extMatrix, &fMatrix);
+ fExtMatrix = &fMatrix;
+ } else {
+ fExtMatrix = NULL;
+ }
+ }
+ const GrMatrix* extMatrix() const { return fExtMatrix; }
+
+private:
+ GrMatrix fMatrix;
+ GrMatrix* fExtMatrix; // NULL or &fMatrix
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGpuDevice::SkAutoCachedTexture::
+ SkAutoCachedTexture(SkGpuDevice* device,
+ const SkBitmap& bitmap,
+ const GrSamplerState& sampler,
+ GrTexture** texture) {
+ GrAssert(texture);
+ fTex = NULL;
+ *texture = this->set(device, bitmap, sampler);
+}
+
+SkGpuDevice::SkAutoCachedTexture::SkAutoCachedTexture() {
+ fTex = NULL;
+}
+
+GrTexture* SkGpuDevice::SkAutoCachedTexture::set(SkGpuDevice* device,
+ const SkBitmap& bitmap,
+ const GrSamplerState& sampler) {
+ if (fTex) {
+ fDevice->unlockCachedTexture(fTex);
+ }
+ fDevice = device;
+ GrTexture* texture = (GrTexture*)bitmap.getTexture();
+ if (texture) {
+ // return the native texture
+ fTex = NULL;
+ device->context()->setTexture(texture);
+ } else {
+ // look it up in our cache
+ fTex = device->lockCachedTexture(bitmap, sampler, &texture, false);
+ }
+ return texture;
+}
+
+SkGpuDevice::SkAutoCachedTexture::~SkAutoCachedTexture() {
+ if (fTex) {
+ fDevice->unlockCachedTexture(fTex);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool gDoTraceDraw;
+
+struct GrSkDrawProcs : public SkDrawProcs {
+public:
+ GrContext* fContext;
+ GrTextContext* fTextContext;
+ GrFontScaler* fFontScaler; // cached in the skia glyphcache
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGpuDevice::SkGpuDevice(SkGpuCanvas* canvas, const SkBitmap& bitmap, bool isLayer)
+ : SkDevice(canvas, bitmap, false) {
+
+ fNeedPrepareRenderTarget = false;
+ fDrawProcs = NULL;
+
+ fContext = canvas->context();
+
+ fCache = NULL;
+ fTexture = NULL;
+ fRenderTarget = NULL;
+ fNeedClear = false;
+
+ if (isLayer) {
+ SkBitmap::Config c = bitmap.config();
+ if (c != SkBitmap::kRGB_565_Config) {
+ c = SkBitmap::kARGB_8888_Config;
+ }
+ SkBitmap bm;
+ bm.setConfig(c, this->width(), this->height());
+
+#if CACHE_LAYER_TEXTURES
+
+ fCache = this->lockCachedTexture(bm, GrSamplerState::ClampNoFilter(),
+ &fTexture, true);
+ if (fCache) {
+ SkASSERT(NULL != fTexture);
+ SkASSERT(fTexture->isRenderTarget());
+ }
+#else
+ const GrGpu::TextureDesc desc = {
+ GrGpu::kRenderTarget_TextureFlag,
+ GrGpu::kNone_AALevel,
+ this->width(),
+ this->height(),
+ SkGr::Bitmap2PixelConfig(bm)
+ };
+
+ fTexture = fContext->createUncachedTexture(desc, NULL, 0);
+#endif
+ if (NULL != fTexture) {
+ fRenderTarget = fTexture->asRenderTarget();
+
+ GrAssert(NULL != fRenderTarget);
+
+ // we defer the actual clear until our gainFocus()
+ fNeedClear = true;
+
+ // wrap the bitmap with a pixelref to expose our texture
+ SkGrTexturePixelRef* pr = new SkGrTexturePixelRef(fTexture);
+ this->setPixelRef(pr, 0)->unref();
+ } else {
+ GrPrintf("--- failed to create gpu-offscreen [%d %d]\n",
+ this->width(), this->height());
+ }
+ }
+
+ if (NULL == fRenderTarget) {
+ GrAssert(NULL == fCache);
+ GrAssert(NULL == fTexture);
+
+ fRenderTarget = fContext->currentRenderTarget();
+ fRenderTarget->ref();
+ fContext->setDefaultRenderTargetSize(this->width(), this->height());
+ }
+}
+
+SkGpuDevice::~SkGpuDevice() {
+ if (fDrawProcs) {
+ delete fDrawProcs;
+ }
+
+ if (fCache) {
+ GrAssert(NULL != fTexture);
+ GrAssert(fRenderTarget == fTexture->asRenderTarget());
+ // IMPORTANT: reattach the rendertarget/tex back to the cache.
+ fContext->reattachAndUnlockCachedTexture((GrTextureEntry*)fCache);
+ } else if (NULL != fTexture) {
+ GrAssert(!CACHE_LAYER_TEXTURES);
+ GrAssert(fRenderTarget == fTexture->asRenderTarget());
+ fTexture->unref();
+ } else if (NULL != fRenderTarget) {
+ fRenderTarget->unref();
+ }
+}
+
+void SkGpuDevice::bindDeviceToTargetHandle(intptr_t handle) {
+ if (fCache) {
+ GrAssert(NULL != fTexture);
+ GrAssert(fRenderTarget == fTexture->asRenderTarget());
+ // IMPORTANT: reattach the rendertarget/tex back to the cache.
+ fContext->reattachAndUnlockCachedTexture((GrTextureEntry*)fCache);
+ } else if (NULL != fTexture) {
+ GrAssert(!CACHE_LAYER_TEXTURES);
+ fTexture->unref();
+ } else if (NULL != fRenderTarget) {
+ fRenderTarget->unref();
+ }
+
+ fCache = NULL;
+ fTexture = NULL;
+ fRenderTarget = fContext->createPlatformRenderTarget(handle,
+ this->width(),
+ this->height());
+}
+
+intptr_t SkGpuDevice::getLayerTextureHandle() const {
+ if (fTexture) {
+ return fTexture->getTextureHandle();
+ } else {
+ return 0;
+ }
+}
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGpuDevice::makeRenderTargetCurrent() {
+ fContext->setRenderTarget(fRenderTarget);
+ fContext->flush(true);
+ fNeedPrepareRenderTarget = true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkGpuDevice::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
+ SkIRect bounds;
+ bounds.set(0, 0, this->width(), this->height());
+ if (!bounds.intersect(srcRect)) {
+ return false;
+ }
+
+ const int w = bounds.width();
+ const int h = bounds.height();
+ SkBitmap tmp;
+ // note we explicitly specify our rowBytes to be snug (no gap between rows)
+ tmp.setConfig(SkBitmap::kARGB_8888_Config, w, h, w * 4);
+ if (!tmp.allocPixels()) {
+ return false;
+ }
+
+ SkAutoLockPixels alp(tmp);
+ fContext->setRenderTarget(fRenderTarget);
+ // we aren't setting the clip or matrix, so mark as dirty
+ // we don't need to set them for this call and don't have them anyway
+ fNeedPrepareRenderTarget = true;
+
+ if (!fContext->readPixels(bounds.fLeft, bounds.fTop,
+ bounds.width(), bounds.height(),
+ GrTexture::kRGBA_8888_PixelConfig,
+ tmp.getPixels())) {
+ return false;
+ }
+
+ tmp.swap(*bitmap);
+ return true;
+}
+
+void SkGpuDevice::writePixels(const SkBitmap& bitmap, int x, int y) {
+ SkAutoLockPixels alp(bitmap);
+ if (!bitmap.readyToDraw()) {
+ return;
+ }
+ GrTexture::PixelConfig config = SkGr::BitmapConfig2PixelConfig(bitmap.config(),
+ bitmap.isOpaque());
+ fContext->setRenderTarget(fRenderTarget);
+ // we aren't setting the clip or matrix, so mark as dirty
+ // we don't need to set them for this call and don't have them anyway
+ fNeedPrepareRenderTarget = true;
+
+ fContext->writePixels(x, y, bitmap.width(), bitmap.height(),
+ config, bitmap.getPixels(), bitmap.rowBytes());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void convert_matrixclip(GrContext* context, const SkMatrix& matrix,
+ const SkRegion& clip) {
+ GrMatrix grmat;
+ SkGr::SkMatrix2GrMatrix(matrix, &grmat);
+ context->setViewMatrix(grmat);
+
+ SkGrClipIterator iter;
+ iter.reset(clip);
+ GrClip grc(&iter);
+ if (context->getClip() == grc) {
+ } else {
+ context->setClip(grc);
+ }
+}
+
+// call this ever each draw call, to ensure that the context reflects our state,
+// and not the state from some other canvas/device
+void SkGpuDevice::prepareRenderTarget(const SkDraw& draw) {
+ if (fNeedPrepareRenderTarget ||
+ fContext->currentRenderTarget() != fRenderTarget) {
+
+ fContext->setRenderTarget(fRenderTarget);
+ convert_matrixclip(fContext, *draw.fMatrix, *draw.fClip);
+ fNeedPrepareRenderTarget = false;
+ }
+}
+
+void SkGpuDevice::setMatrixClip(const SkMatrix& matrix, const SkRegion& clip) {
+ this->INHERITED::setMatrixClip(matrix, clip);
+
+ convert_matrixclip(fContext, matrix, clip);
+}
+
+void SkGpuDevice::gainFocus(SkCanvas* canvas, const SkMatrix& matrix,
+ const SkRegion& clip) {
+ fContext->setRenderTarget(fRenderTarget);
+
+ this->INHERITED::gainFocus(canvas, matrix, clip);
+
+ convert_matrixclip(fContext, matrix, clip);
+
+ if (fNeedClear) {
+ fContext->eraseColor(0x0);
+ fNeedClear = false;
+ }
+}
+
+bool SkGpuDevice::bindDeviceAsTexture(SkPoint* max) {
+ if (NULL != fTexture) {
+ fContext->setTexture(fTexture);
+ if (NULL != max) {
+ max->set(SkFixedToScalar((width() << 16) /
+ fTexture->allocWidth()),
+ SkFixedToScalar((height() << 16) /
+ fTexture->allocHeight()));
+ }
+ return true;
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// must be in the same order as SkXfermode::Coeff in SkXfermode.h
+
+SkGpuDevice::AutoPaintShader::AutoPaintShader() {
+ fSuccess = false;
+ fTexture = NULL;
+}
+
+SkGpuDevice::AutoPaintShader::AutoPaintShader(SkGpuDevice* device,
+ const SkPaint& paint,
+ const SkMatrix& matrix) {
+ fSuccess = false;
+ fTexture = NULL;
+ this->init(device, paint, matrix);
+}
+
+void SkGpuDevice::AutoPaintShader::init(SkGpuDevice* device,
+ const SkPaint& paint,
+ const SkMatrix& ctm) {
+ fSuccess = true;
+ GrContext* ctx = device->context();
+ sk_gr_set_paint(ctx, paint); // should we pass true for justAlpha if we have a shader/texture?
+
+ SkShader* shader = paint.getShader();
+ if (NULL == shader) {
+ return;
+ }
+
+ if (!shader->setContext(device->accessBitmap(false), paint, ctm)) {
+ fSuccess = false;
+ return;
+ }
+
+ GrSamplerState::SampleMode sampleMode;
+ SkBitmap bitmap;
+ SkMatrix matrix;
+ SkShader::TileMode tileModes[2];
+ SkScalar twoPointParams[3];
+ SkShader::BitmapType bmptype = shader->asABitmap(&bitmap, &matrix,
+ tileModes, twoPointParams);
+
+ switch (bmptype) {
+ case SkShader::kNone_BitmapType:
+ SkDebugf("shader->asABitmap() == kNone_BitmapType");
+ return;
+ case SkShader::kDefault_BitmapType:
+ sampleMode = GrSamplerState::kNormal_SampleMode;
+ break;
+ case SkShader::kRadial_BitmapType:
+ sampleMode = GrSamplerState::kRadial_SampleMode;
+ break;
+ case SkShader::kSweep_BitmapType:
+ sampleMode = GrSamplerState::kSweep_SampleMode;
+ break;
+ case SkShader::kTwoPointRadial_BitmapType:
+ sampleMode = GrSamplerState::kRadial2_SampleMode;
+ break;
+ default:
+ SkASSERT("Unexpected return from asABitmap");
+ return;
+ }
+
+ bitmap.lockPixels();
+ if (!bitmap.getTexture() && !bitmap.readyToDraw()) {
+ return;
+ }
+
+ // see if we've already cached the bitmap from the shader
+ GrSamplerState samplerState(sk_tile_mode_to_grwrap(tileModes[0]),
+ sk_tile_mode_to_grwrap(tileModes[1]),
+ sampleMode,
+ paint.isFilterBitmap());
+
+ if (GrSamplerState::kRadial2_SampleMode == sampleMode) {
+ samplerState.setRadial2Params(twoPointParams[0],
+ twoPointParams[1],
+ twoPointParams[2] < 0);
+ }
+
+ GrTexture* texture = fCachedTexture.set(device, bitmap, samplerState);
+ if (NULL == texture) {
+ return;
+ }
+
+ // the lock has already called setTexture for us
+ ctx->setSamplerState(samplerState);
+
+ // 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);
+ }
+ }
+ if (SkShader::kDefault_BitmapType == bmptype) {
+ GrScalar sx = (GR_Scalar1 * texture->contentWidth()) /
+ (bitmap.width() * texture->allocWidth());
+ GrScalar sy = (GR_Scalar1 * texture->contentHeight()) /
+ (bitmap.height() * texture->allocHeight());
+ matrix.postScale(sx, sy);
+
+ } else if (SkShader::kRadial_BitmapType == bmptype) {
+ GrScalar s = (GR_Scalar1 * texture->contentWidth()) /
+ (bitmap.width() * texture->allocWidth());
+ matrix.postScale(s, s);
+ }
+ GrMatrix grmat;
+ SkGr::SkMatrix2GrMatrix(matrix, &grmat);
+ ctx->setTextureMatrix(grmat);
+
+ // since we're going to use a shader/texture, we don't want the color,
+ // just its alpha
+ ctx->setAlpha(paint.getAlpha());
+ // report that we have setup the texture
+ fSuccess = true;
+ fTexture = texture;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGpuDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
+ CHECK_SHOULD_DRAW(draw);
+
+ AutoPaintShader shader(this, paint, *draw.fMatrix);
+ if (shader.failed()) {
+ return;
+ }
+ fContext->drawFull(shader.useTex());
+}
+
+// must be in SkCanvas::PointMode order
+static const GrGpu::PrimitiveType gPointMode2PrimtiveType[] = {
+ GrGpu::kPoints_PrimitiveType,
+ GrGpu::kLines_PrimitiveType,
+ GrGpu::kLineStrip_PrimitiveType
+};
+
+void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
+ size_t count, const SkPoint pts[], const SkPaint& paint) {
+ CHECK_SHOULD_DRAW(draw);
+
+ SkScalar width = paint.getStrokeWidth();
+ if (width < 0) {
+ return;
+ }
+
+ // we only handle hairlines here, else we let the SkDraw call our drawPath()
+ if (width > 0) {
+ draw.drawPoints(mode, count, pts, paint, true);
+ return;
+ }
+
+ AutoPaintShader shader(this, paint, *draw.fMatrix);
+ if (shader.failed()) {
+ return;
+ }
+
+ GrVertexLayout layout = shader.useTex() ?
+ GrDrawTarget::kPositionAsTexCoord_VertexLayoutBit :
+ 0;
+#if SK_SCALAR_IS_GR_SCALAR
+ fContext->setVertexSourceToArray(pts, layout);
+ fContext->drawNonIndexed(gPointMode2PrimtiveType[mode], 0, count);
+#else
+ GrPoint* v;
+ fContext->reserveAndLockGeometry(layout, count, 0, (void**)&v, NULL);
+ for (int i = 0; i < count; ++i) {
+ v[i].set(SkScalarToGrScalar(pts[i].fX), SkScalarToGrScalar(pts[i].fY));
+ }
+ fContext->drawNonIndexed(gPointMode2PrimtiveType[mode], layout, 0, count);
+ fContext->releaseReservedGeometry();
+#endif
+
+}
+
+void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect,
+ const SkPaint& paint) {
+ CHECK_SHOULD_DRAW(draw);
+
+ bool doStroke = paint.getStyle() == SkPaint::kStroke_Style;
+ SkScalar width = paint.getStrokeWidth();
+
+ /*
+ We have special code for hairline strokes, miter-strokes, and fills.
+ Anything else we just call our path code. (i.e. non-miter thick stroke)
+ */
+ if (doStroke && width > 0 && paint.getStrokeJoin() != SkPaint::kMiter_Join) {
+ SkPath path;
+ path.addRect(rect);
+ this->drawPath(draw, path, paint, NULL, true);
+ return;
+ }
+
+ AutoPaintShader shader(this, paint, *draw.fMatrix);
+ if (shader.failed()) {
+ return;
+ }
+
+ fContext->drawRect(Sk2Gr(rect), shader.useTex(), doStroke ? width : -1);
+}
+
+void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& path,
+ const SkPaint& paint, const SkMatrix* prePathMatrix,
+ bool pathIsMutable) {
+ CHECK_SHOULD_DRAW(draw);
+
+ AutoPaintShader shader(this, paint, *draw.fMatrix);
+ if (shader.failed()) {
+ return;
+ }
+
+ const SkPath* pathPtr = &path;
+ SkPath tmpPath;
+
+ if (prePathMatrix) {
+ if (pathIsMutable) {
+ const_cast<SkPath*>(pathPtr)->transform(*prePathMatrix);
+ } else {
+ path.transform(*prePathMatrix, &tmpPath);
+ pathPtr = &tmpPath;
+ }
+ }
+
+ SkPath fillPath;
+ GrContext::PathFills fill = GrContext::kHairLine_PathFill;
+
+ if (paint.getFillPath(*pathPtr, &fillPath)) {
+ switch (fillPath.getFillType()) {
+ case SkPath::kWinding_FillType:
+ fill = GrContext::kWinding_PathFill;
+ break;
+ case SkPath::kEvenOdd_FillType:
+ fill = GrContext::kEvenOdd_PathFill;
+ break;
+ case SkPath::kInverseWinding_FillType:
+ fill = GrContext::kInverseWinding_PathFill;
+ break;
+ case SkPath::kInverseEvenOdd_FillType:
+ fill = GrContext::kInverseEvenOdd_PathFill;
+ break;
+ default:
+ SkDebugf("Unsupported path fill type");
+ return;
+ }
+ }
+
+ SkGrPathIter iter(fillPath);
+ fContext->drawPath(&iter, fill, shader.useTex());
+}
+
+/*
+ * This value must not exceed the GPU's texture dimension limit, but it can
+ * be smaller, if that helps avoid very large single textures hurting the
+ * cache.
+ */
+#define MAX_TEXTURE_DIM 512
+
+void SkGpuDevice::drawBitmap(const SkDraw& draw,
+ const SkBitmap& bitmap,
+ const SkIRect* srcRectPtr,
+ const SkMatrix& m,
+ const SkPaint& paint) {
+ CHECK_SHOULD_DRAW(draw);
+
+ SkIRect srcRect;
+ if (NULL == srcRectPtr) {
+ srcRect.set(0, 0, bitmap.width(), bitmap.height());
+ } else {
+ srcRect = *srcRectPtr;
+ }
+
+ if (bitmap.getTexture() || (bitmap.width() <= MAX_TEXTURE_DIM &&
+ bitmap.height() <= MAX_TEXTURE_DIM)) {
+ // take the fast case
+ this->internalDrawBitmap(draw, bitmap, srcRect, m, paint);
+ return;
+ }
+
+ // undo the translate done by SkCanvas
+ int DX = SkMax32(0, srcRect.fLeft);
+ int DY = SkMax32(0, srcRect.fTop);
+ // compute clip bounds in local coordinates
+ SkIRect clipRect;
+ {
+ SkRect r;
+ r.set(draw.fClip->getBounds());
+ SkMatrix matrix, inverse;
+ matrix.setConcat(*draw.fMatrix, m);
+ if (!matrix.invert(&inverse)) {
+ return;
+ }
+ inverse.mapRect(&r);
+ r.roundOut(&clipRect);
+ // apply the canvas' translate to our local clip
+ clipRect.offset(DX, DY);
+ }
+
+ int nx = bitmap.width() / MAX_TEXTURE_DIM;
+ int ny = bitmap.height() / MAX_TEXTURE_DIM;
+ for (int x = 0; x <= nx; x++) {
+ for (int y = 0; y <= ny; y++) {
+ SkIRect tileR;
+ tileR.set(x * MAX_TEXTURE_DIM, y * MAX_TEXTURE_DIM,
+ (x + 1) * MAX_TEXTURE_DIM, (y + 1) * MAX_TEXTURE_DIM);
+ if (!SkIRect::Intersects(tileR, clipRect)) {
+ continue;
+ }
+
+ SkIRect srcR = tileR;
+ if (!srcR.intersect(srcRect)) {
+ continue;
+ }
+
+ SkBitmap tmpB;
+ if (bitmap.extractSubset(&tmpB, tileR)) {
+ // now offset it to make it "local" to our tmp bitmap
+ srcR.offset(-tileR.fLeft, -tileR.fTop);
+
+ SkMatrix tmpM(m);
+ {
+ int dx = tileR.fLeft - DX + SkMax32(0, srcR.fLeft);
+ int dy = tileR.fTop - DY + SkMax32(0, srcR.fTop);
+ tmpM.preTranslate(SkIntToScalar(dx), SkIntToScalar(dy));
+ }
+ this->internalDrawBitmap(draw, tmpB, srcR, tmpM, paint);
+ }
+ }
+ }
+}
+
+/*
+ * This is called by drawBitmap(), which has to handle images that may be too
+ * large to be represented by a single texture.
+ *
+ * internalDrawBitmap assumes that the specified bitmap will fit in a texture.
+ */
+void SkGpuDevice::internalDrawBitmap(const SkDraw& draw,
+ const SkBitmap& bitmap,
+ const SkIRect& srcRect,
+ const SkMatrix& m,
+ const SkPaint& paint) {
+ SkASSERT(bitmap.width() <= MAX_TEXTURE_DIM &&
+ bitmap.height() <= MAX_TEXTURE_DIM);
+
+ SkAutoLockPixels alp(bitmap);
+ if (!bitmap.getTexture() && !bitmap.readyToDraw()) {
+ return;
+ }
+
+ GrSamplerState sampler(paint.isFilterBitmap()); // defaults to clamp
+ // the lock has already called setTexture for us
+ fContext->setSamplerState(sampler);
+
+ GrTexture* texture;
+ SkAutoCachedTexture act(this, bitmap, sampler, &texture);
+ if (NULL == texture) {
+ return;
+ }
+
+ GrVertexLayout layout = GrDrawTarget::kSeparateTexCoord_VertexLayoutBit;
+
+ GrPoint* vertex;
+ if (!fContext->reserveAndLockGeometry(layout, 4,
+ 0, (void**)&vertex, NULL)) {
+ return;
+ }
+
+ {
+ GrMatrix grmat;
+ SkGr::SkMatrix2GrMatrix(m, &grmat);
+ vertex[0].setIRectFan(0, 0, srcRect.width(), srcRect.height(),
+ 2*sizeof(GrPoint));
+ grmat.mapPointsWithStride(vertex, 2*sizeof(GrPoint), 4);
+ }
+
+ SkScalar left = SkFixedToScalar((srcRect.fLeft << 16) /
+ texture->allocWidth());
+ SkScalar right = SkFixedToScalar((srcRect.fRight << 16) /
+ texture->allocWidth());
+ SkScalar top = SkFixedToScalar((srcRect.fTop << 16) /
+ texture->allocHeight());
+ SkScalar bottom = SkFixedToScalar((srcRect.fBottom << 16) /
+ texture->allocHeight());
+ vertex[1].setRectFan(left, top, right, bottom, 2*sizeof(GrPoint));
+
+ fContext->setTextureMatrix(GrMatrix::I());
+ // now draw the mesh
+ sk_gr_set_paint(fContext, paint, true);
+ fContext->drawNonIndexed(GrGpu::kTriangleFan_PrimitiveType, 0, 4);
+ fContext->releaseReservedGeometry();
+}
+
+static void gl_drawSprite(GrContext* ctx,
+ int x, int y, int w, int h, const SkPoint& max,
+ const SkPaint& paint) {
+ GrAutoViewMatrix avm(ctx, GrMatrix::I());
+
+ ctx->setSamplerState(GrSamplerState::ClampNoFilter());
+ ctx->setTextureMatrix(GrMatrix::I());
+
+ GrPoint* vertex;
+ GrVertexLayout layout = GrGpu::kSeparateTexCoord_VertexLayoutBit;
+ if (!ctx->reserveAndLockGeometry(layout, 4, 0, (void**)&vertex, NULL)) {
+ return;
+ }
+
+ vertex[1].setRectFan(0, 0, max.fX, max.fY, 2*sizeof(GrPoint));
+
+ vertex[0].setIRectFan(x, y, x + w, y + h, 2*sizeof(GrPoint));
+
+ sk_gr_set_paint(ctx, paint, true);
+ // should look to use glDrawTexi() has we do for text...
+ ctx->drawNonIndexed(GrGpu::kTriangleFan_PrimitiveType, 0, 4);
+ ctx->releaseReservedGeometry();
+}
+
+void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
+ int left, int top, const SkPaint& paint) {
+ CHECK_SHOULD_DRAW(draw);
+
+ SkAutoLockPixels alp(bitmap);
+ if (!bitmap.getTexture() && !bitmap.readyToDraw()) {
+ return;
+ }
+
+ SkPoint max;
+ GrTexture* texture;
+ SkAutoCachedTexture act(this, bitmap, GrSamplerState::ClampNoFilter(),
+ &texture);
+
+ max.set(SkFixedToScalar((texture->contentWidth() << 16) /
+ texture->allocWidth()),
+ SkFixedToScalar((texture->contentHeight() << 16) /
+ texture->allocHeight()));
+ gl_drawSprite(fContext, left, top, bitmap.width(), bitmap.height(), max, paint);
+}
+
+void SkGpuDevice::drawDevice(const SkDraw& draw, SkDevice* dev,
+ int x, int y, const SkPaint& paint) {
+ CHECK_SHOULD_DRAW(draw);
+
+ SkPoint max;
+ if (((SkGpuDevice*)dev)->bindDeviceAsTexture(&max)) {
+ const SkBitmap& bm = dev->accessBitmap(false);
+ int w = bm.width();
+ int h = bm.height();
+ gl_drawSprite(fContext, x, y, w, h, max, paint);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// must be in SkCanvas::VertexMode order
+static const GrGpu::PrimitiveType gVertexMode2PrimitiveType[] = {
+ GrGpu::kTriangles_PrimitiveType,
+ GrGpu::kTriangleStrip_PrimitiveType,
+ GrGpu::kTriangleFan_PrimitiveType,
+};
+
+void SkGpuDevice::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) {
+ CHECK_SHOULD_DRAW(draw);
+
+ sk_gr_set_paint(fContext, paint);
+
+ TexCache* cache = NULL;
+
+ bool useTexture = false;
+
+ AutoPaintShader autoShader;
+
+ if (texs) {
+ autoShader.init(this, paint, *draw.fMatrix);
+
+ if (autoShader.failed()) {
+ return;
+ }
+ useTexture = autoShader.useTex();
+ }
+
+ bool releaseVerts = false;
+ GrVertexLayout layout = 0;
+ if (useTexture) {
+ layout |= GrDrawTarget::kSeparateTexCoord_VertexLayoutBit;
+ }
+ if (NULL != colors) {
+ layout |= GrDrawTarget::kColor_VertexLayoutBit;
+ }
+
+ #if SK_SCALAR_IS_GR_SCALAR
+ if (!layout) {
+ fContext->setVertexSourceToArray(vertices, layout);
+ } else
+ #endif
+ {
+ void* verts;
+ releaseVerts = true;
+ if (!fContext->reserveAndLockGeometry(layout, vertexCount, 0,
+ &verts, NULL)) {
+ return;
+ }
+ int texOffset, colorOffset;
+ uint32_t stride = GrDrawTarget::VertexSizeAndOffsets(layout,
+ &texOffset,
+ &colorOffset);
+ for (int i = 0; i < vertexCount; ++i) {
+ GrPoint* p = (GrPoint*)((intptr_t)verts + i * stride);
+ p->set(SkScalarToGrScalar(vertices[i].fX),
+ SkScalarToGrScalar(vertices[i].fY));
+ if (texOffset > 0) {
+ GrPoint* t = (GrPoint*)((intptr_t)p + texOffset);
+ t->set(SkScalarToGrScalar(texs[i].fX),
+ SkScalarToGrScalar(texs[i].fY));
+ }
+ if (colorOffset > 0) {
+ uint32_t* color = (uint32_t*) ((intptr_t)p + colorOffset);
+ *color = SkGr::SkColor2GrColor(colors[i]);
+ }
+ }
+ }
+ if (indices) {
+ fContext->setIndexSourceToArray(indices);
+ fContext->drawIndexed(gVertexMode2PrimitiveType[vmode], 0, 0,
+ vertexCount, indexCount);
+ } else {
+ fContext->drawNonIndexed(gVertexMode2PrimitiveType[vmode],
+ 0, vertexCount);
+ }
+ if (cache) {
+ this->unlockCachedTexture(cache);
+ }
+ if (releaseVerts) {
+ fContext->releaseReservedGeometry();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void GlyphCacheAuxProc(void* data) {
+ delete (GrFontScaler*)data;
+}
+
+static GrFontScaler* get_gr_font_scaler(SkGlyphCache* cache) {
+ void* auxData;
+ GrFontScaler* scaler = NULL;
+ if (cache->getAuxProcData(GlyphCacheAuxProc, &auxData)) {
+ scaler = (GrFontScaler*)auxData;
+ }
+ if (NULL == scaler) {
+ scaler = new SkGrFontScaler(cache);
+ cache->setAuxProc(GlyphCacheAuxProc, scaler);
+ }
+ return scaler;
+}
+
+static void SkGPU_Draw1Glyph(const SkDraw1Glyph& state,
+ SkFixed fx, SkFixed fy,
+ const SkGlyph& glyph) {
+ SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
+
+ GrSkDrawProcs* procs = (GrSkDrawProcs*)state.fDraw->fProcs;
+
+ if (NULL == procs->fFontScaler) {
+ procs->fFontScaler = get_gr_font_scaler(state.fCache);
+ }
+ procs->fTextContext->drawPackedGlyph(GrGlyph::Pack(glyph.getGlyphID(), fx, 0),
+ SkIntToFixed(SkFixedFloor(fx)), fy,
+ procs->fFontScaler);
+}
+
+SkDrawProcs* SkGpuDevice::initDrawForText(const SkPaint& paint,
+ GrTextContext* context) {
+
+ // deferred allocation
+ if (NULL == fDrawProcs) {
+ fDrawProcs = new GrSkDrawProcs;
+ fDrawProcs->fD1GProc = SkGPU_Draw1Glyph;
+ fDrawProcs->fContext = fContext;
+ }
+
+ // init our (and GL's) state
+ fDrawProcs->fTextContext = context;
+ fDrawProcs->fFontScaler = NULL;
+ return fDrawProcs;
+}
+
+void SkGpuDevice::drawText(const SkDraw& draw, const void* text,
+ size_t byteLength, SkScalar x, SkScalar y,
+ const SkPaint& paint) {
+ CHECK_SHOULD_DRAW(draw);
+
+ if (draw.fMatrix->getType() & SkMatrix::kPerspective_Mask) {
+ // this guy will just call our drawPath()
+ draw.drawText((const char*)text, byteLength, x, y, paint);
+ } else {
+ SkAutoExtMatrix aem(draw.fExtMatrix);
+ SkDraw myDraw(draw);
+ sk_gr_set_paint(fContext, paint);
+ GrTextContext context(fContext, aem.extMatrix());
+ myDraw.fProcs = this->initDrawForText(paint, &context);
+ this->INHERITED::drawText(myDraw, text, byteLength, x, y, paint);
+ }
+}
+
+void SkGpuDevice::drawPosText(const SkDraw& draw, const void* text,
+ size_t byteLength, const SkScalar pos[],
+ SkScalar constY, int scalarsPerPos,
+ const SkPaint& paint) {
+ CHECK_SHOULD_DRAW(draw);
+
+ if (draw.fMatrix->getType() & SkMatrix::kPerspective_Mask) {
+ // this guy will just call our drawPath()
+ draw.drawPosText((const char*)text, byteLength, pos, constY,
+ scalarsPerPos, paint);
+ } else {
+ SkAutoExtMatrix aem(draw.fExtMatrix);
+ SkDraw myDraw(draw);
+ sk_gr_set_paint(fContext, paint);
+ GrTextContext context(fContext, aem.extMatrix());
+ myDraw.fProcs = this->initDrawForText(paint, &context);
+ this->INHERITED::drawPosText(myDraw, text, byteLength, pos, constY,
+ scalarsPerPos, paint);
+ }
+}
+
+void SkGpuDevice::drawTextOnPath(const SkDraw& draw, const void* text,
+ size_t len, const SkPath& path,
+ const SkMatrix* m, const SkPaint& paint) {
+ CHECK_SHOULD_DRAW(draw);
+
+ SkASSERT(draw.fDevice == this);
+ draw.drawTextOnPath((const char*)text, len, path, m, paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGpuDevice::TexCache* SkGpuDevice::lockCachedTexture(const SkBitmap& bitmap,
+ const GrSamplerState& sampler,
+ GrTexture** texture,
+ bool forDeviceRenderTarget) {
+ GrContext* ctx = this->context();
+ uint32_t p0, p1;
+ if (forDeviceRenderTarget) {
+ p0 = p1 = -1;
+ } else {
+ p0 = bitmap.getGenerationID();
+ p1 = bitmap.pixelRefOffset();
+ }
+
+ GrTexture* newTexture = NULL;
+ GrTextureKey key(p0, p1, bitmap.width(), bitmap.height());
+ GrTextureEntry* entry = ctx->findAndLockTexture(&key, sampler);
+
+ if (NULL == entry) {
+
+ if (forDeviceRenderTarget) {
+ const GrGpu::TextureDesc desc = {
+ GrGpu::kRenderTarget_TextureFlag,
+ GrGpu::kNone_AALevel,
+ bitmap.width(),
+ bitmap.height(),
+ SkGr::Bitmap2PixelConfig(bitmap)
+ };
+ entry = ctx->createAndLockTexture(&key, sampler, desc, NULL, 0);
+
+ } else {
+ entry = sk_gr_create_bitmap_texture(ctx, &key, sampler, bitmap);
+ }
+ if (NULL == entry) {
+ GrPrintf("---- failed to create texture for cache [%d %d]\n",
+ bitmap.width(), bitmap.height());
+ }
+ }
+
+ if (NULL != entry) {
+ newTexture = entry->texture();
+ ctx->setTexture(newTexture);
+ if (texture) {
+ *texture = newTexture;
+ }
+ // IMPORTANT: We can't allow another SkGpuDevice to get this
+ // cache entry until this one is destroyed!
+ if (forDeviceRenderTarget) {
+ ctx->detachCachedTexture(entry);
+ }
+ }
+ return (TexCache*)entry;
+}
+
+void SkGpuDevice::unlockCachedTexture(TexCache* cache) {
+ this->context()->unlockTexture((GrTextureEntry*)cache);
+}
+
+