/* * Copyright 2012 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkPictureUtils.h" #include "SkCanvas.h" #include "SkData.h" #include "SkDevice.h" #include "SkPixelRef.h" #include "SkShader.h" #include "SkRRect.h" class PixelRefSet { public: PixelRefSet(SkTDArray* array) : fArray(array) {} // This does a linear search on existing pixelrefs, so if this list gets big // we should use a more complex sorted/hashy thing. // void add(SkPixelRef* pr) { uint32_t genID = pr->getGenerationID(); if (fGenID.find(genID) < 0) { *fArray->append() = pr; *fGenID.append() = genID; // SkDebugf("--- adding [%d] %x %d\n", fArray->count() - 1, pr, genID); } else { // SkDebugf("--- already have %x %d\n", pr, genID); } } private: SkTDArray* fArray; SkTDArray fGenID; }; static void not_supported() { SkASSERT(!"this method should never be called"); } static void nothing_to_do() {} /** * This device will route all bitmaps (primitives and in shaders) to its PRSet. * It should never actually draw anything, so there need not be any pixels * behind its device-bitmap. */ class GatherPixelRefDevice : public SkDevice { private: PixelRefSet* fPRSet; void addBitmap(const SkBitmap& bm) { fPRSet->add(bm.pixelRef()); } void addBitmapFromPaint(const SkPaint& paint) { SkShader* shader = paint.getShader(); if (shader) { SkBitmap bm; // Check whether the shader is a gradient in order to short-circuit // call to asABitmap to prevent generation of bitmaps from // gradient shaders, which implement asABitmap. if (SkShader::kNone_GradientType == shader->asAGradient(NULL) && shader->asABitmap(&bm, NULL, NULL)) { fPRSet->add(bm.pixelRef()); } } } public: GatherPixelRefDevice(const SkBitmap& bm, PixelRefSet* prset) : SkDevice(bm) { fPRSet = prset; } virtual void clear(SkColor color) SK_OVERRIDE { nothing_to_do(); } virtual void writePixels(const SkBitmap& bitmap, int x, int y, SkCanvas::Config8888 config8888) SK_OVERRIDE { not_supported(); } virtual void drawPaint(const SkDraw&, const SkPaint& paint) SK_OVERRIDE { this->addBitmapFromPaint(paint); } virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count, const SkPoint[], const SkPaint& paint) SK_OVERRIDE { this->addBitmapFromPaint(paint); } virtual void drawRect(const SkDraw&, const SkRect&, const SkPaint& paint) SK_OVERRIDE { this->addBitmapFromPaint(paint); } virtual void drawOval(const SkDraw&, const SkRect&, const SkPaint& paint) SK_OVERRIDE { this->addBitmapFromPaint(paint); } virtual void drawPath(const SkDraw&, const SkPath& path, const SkPaint& paint, const SkMatrix* prePathMatrix, bool pathIsMutable) SK_OVERRIDE { this->addBitmapFromPaint(paint); } virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap, const SkIRect* srcRectOrNull, const SkMatrix&, const SkPaint&) SK_OVERRIDE { this->addBitmap(bitmap); } virtual void drawBitmapRect(const SkDraw&, const SkBitmap& bitmap, const SkRect* srcOrNull, const SkRect& dst, const SkPaint&) SK_OVERRIDE { this->addBitmap(bitmap); } virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap, int x, int y, const SkPaint& paint) SK_OVERRIDE { this->addBitmap(bitmap); } virtual void drawText(const SkDraw&, const void* text, size_t len, SkScalar x, SkScalar y, const SkPaint& paint) SK_OVERRIDE { this->addBitmapFromPaint(paint); } virtual void drawPosText(const SkDraw&, const void* text, size_t len, const SkScalar pos[], SkScalar constY, int, const SkPaint& paint) SK_OVERRIDE { this->addBitmapFromPaint(paint); } virtual void drawTextOnPath(const SkDraw&, const void* text, size_t len, const SkPath& path, const SkMatrix* matrix, const SkPaint& paint) SK_OVERRIDE { this->addBitmapFromPaint(paint); } virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode, int vertexCount, const SkPoint verts[], const SkPoint texs[], const SkColor colors[], SkXfermode* xmode, const uint16_t indices[], int indexCount, const SkPaint& paint) SK_OVERRIDE { this->addBitmapFromPaint(paint); } virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y, const SkPaint&) SK_OVERRIDE { nothing_to_do(); } protected: virtual bool onReadPixels(const SkBitmap& bitmap, int x, int y, SkCanvas::Config8888 config8888) SK_OVERRIDE { not_supported(); return false; } }; class NoSaveLayerCanvas : public SkCanvas { public: NoSaveLayerCanvas(SkDevice* device) : INHERITED(device) {} // turn saveLayer() into save() for speed, should not affect correctness. virtual int saveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags) SK_OVERRIDE { // Like SkPictureRecord, we don't want to create layers, but we do need // to respect the save and (possibly) its rect-clip. int count = this->INHERITED::save(flags); if (bounds) { this->INHERITED::clipRectBounds(bounds, flags, NULL); } return count; } // disable aa for speed virtual bool clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) SK_OVERRIDE { return this->INHERITED::clipRect(rect, op, false); } // for speed, just respect the bounds, and disable AA. May give us a few // false positives and negatives. virtual bool clipPath(const SkPath& path, SkRegion::Op op, bool doAA) SK_OVERRIDE { return this->INHERITED::clipRect(path.getBounds(), op, false); } virtual bool clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) SK_OVERRIDE { return this->INHERITED::clipRect(rrect.getBounds(), op, false); } private: typedef SkCanvas INHERITED; }; SkData* SkPictureUtils::GatherPixelRefs(SkPicture* pict, const SkRect& area) { if (NULL == pict) { return NULL; } // this test also handles if either area or pict's width/height are empty if (!SkRect::Intersects(area, SkRect::MakeWH(SkIntToScalar(pict->width()), SkIntToScalar(pict->height())))) { return NULL; } SkTDArray array; PixelRefSet prset(&array); SkBitmap emptyBitmap; emptyBitmap.setConfig(SkBitmap::kARGB_8888_Config, pict->width(), pict->height()); // note: we do not set any pixels (shouldn't need to) GatherPixelRefDevice device(emptyBitmap, &prset); NoSaveLayerCanvas canvas(&device); canvas.clipRect(area, SkRegion::kIntersect_Op, false); canvas.drawPicture(*pict); SkData* data = NULL; int count = array.count(); if (count > 0) { data = SkData::NewFromMalloc(array.detach(), count * sizeof(SkPixelRef*)); } return data; }