diff options
author | reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2010-12-23 15:00:45 +0000 |
---|---|---|
committer | reed@google.com <reed@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81> | 2010-12-23 15:00:45 +0000 |
commit | 873cb1e23b64e4b2d11749352b626bcef204bdd7 (patch) | |
tree | 16749ff8667f669deb28a00bbb5a7ce6d8db8f0d /src/gpu/SkGpuDevice.cpp | |
parent | 5b9cfd4d8809d6d55aaf047a6a8acc2de2f2eeb0 (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.cpp | 1048 |
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); +} + + |