diff options
Diffstat (limited to 'skia/ext/lazy_pixel_ref_utils.cc')
-rw-r--r-- | skia/ext/lazy_pixel_ref_utils.cc | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/skia/ext/lazy_pixel_ref_utils.cc b/skia/ext/lazy_pixel_ref_utils.cc new file mode 100644 index 0000000..87b487e --- /dev/null +++ b/skia/ext/lazy_pixel_ref_utils.cc @@ -0,0 +1,417 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "lazy_pixel_ref_utils.h" + +#include "SkCanvas.h" +#include "SkData.h" +#include "SkDevice.h" +#include "SkDraw.h" +#include "SkPixelRef.h" +#include "SkRRect.h" +#include "SkRasterClip.h" +#include "SkRect.h" +#include "SkShader.h" + +#include "lazy_pixel_ref.h" + +namespace skia { + +namespace { + +// URI label for a lazily decoded SkPixelRef. +const char kLabelLazyDecoded[] = "lazy"; + +class LazyPixelRefSet { + public: + LazyPixelRefSet( + std::vector<LazyPixelRefUtils::PositionLazyPixelRef>* pixel_refs) + : pixel_refs_(pixel_refs) {} + + void add(SkPixelRef* pixel_ref, const SkRect& rect) { + // Only save lazy pixel refs. + if (pixel_ref->getURI() && + !strcmp(pixel_ref->getURI(), kLabelLazyDecoded)) { + LazyPixelRefUtils::PositionLazyPixelRef position_pixel_ref; + position_pixel_ref.lazy_pixel_ref = + static_cast<skia::LazyPixelRef*>(pixel_ref); + position_pixel_ref.pixel_ref_rect = rect; + pixel_refs_->push_back(position_pixel_ref); + } + } + + private: + std::vector<LazyPixelRefUtils::PositionLazyPixelRef>* pixel_refs_; +}; + +class GatherPixelRefDevice : public SkDevice { + public: + GatherPixelRefDevice(const SkBitmap& bm, LazyPixelRefSet* lazy_pixel_ref_set) + : SkDevice(bm), lazy_pixel_ref_set_(lazy_pixel_ref_set) {} + + virtual void clear(SkColor color) SK_OVERRIDE {} + virtual void writePixels(const SkBitmap& bitmap, + int x, + int y, + SkCanvas::Config8888 config8888) SK_OVERRIDE {} + + virtual void drawPaint(const SkDraw& draw, const SkPaint& paint) SK_OVERRIDE { + SkBitmap bitmap; + if (getBitmapFromPaint(paint, &bitmap)) { + SkRect clip_rect = SkRect::Make(draw.fRC->getBounds()); + SkRect canvas_rect = SkRect::MakeWH(width(), height()); + SkRect paint_rect = SkRect::MakeEmpty(); + paint_rect.intersect(canvas_rect, clip_rect); + + addBitmap(bitmap, paint_rect); + } + } + + virtual void drawPoints(const SkDraw& draw, + SkCanvas::PointMode mode, + size_t count, + const SkPoint points[], + const SkPaint& paint) SK_OVERRIDE { + SkBitmap bitmap; + if (!getBitmapFromPaint(paint, &bitmap)) + return; + + if (count == 0) + return; + + SkPoint min_point = points[0]; + SkPoint max_point = points[0]; + for (size_t i = 1; i < count; ++i) { + const SkPoint& point = points[i]; + min_point.set(std::min(min_point.x(), point.x()), + std::min(min_point.y(), point.y())); + max_point.set(std::max(max_point.x(), point.x()), + std::max(max_point.y(), point.y())); + } + + SkRect bounds = SkRect::MakeLTRB( + min_point.x(), min_point.y(), max_point.x(), max_point.y()); + + GatherPixelRefDevice::drawRect(draw, bounds, paint); + } + virtual void drawRect(const SkDraw& draw, + const SkRect& rect, + const SkPaint& paint) SK_OVERRIDE { + SkBitmap bitmap; + if (getBitmapFromPaint(paint, &bitmap)) { + SkRect mapped_rect; + draw.fMatrix->mapRect(&mapped_rect, rect); + mapped_rect.intersect(SkRect::Make(draw.fRC->getBounds())); + addBitmap(bitmap, mapped_rect); + } + } + virtual void drawOval(const SkDraw& draw, + const SkRect& rect, + const SkPaint& paint) SK_OVERRIDE { + GatherPixelRefDevice::drawRect(draw, rect, paint); + } + virtual void drawRRect(const SkDraw& draw, + const SkRRect& rect, + const SkPaint& paint) SK_OVERRIDE { + GatherPixelRefDevice::drawRect(draw, rect.rect(), paint); + } + virtual void drawPath(const SkDraw& draw, + const SkPath& path, + const SkPaint& paint, + const SkMatrix* prePathMatrix, + bool pathIsMutable) SK_OVERRIDE { + SkBitmap bitmap; + if (!getBitmapFromPaint(paint, &bitmap)) + return; + + SkRect path_bounds = path.getBounds(); + SkRect final_rect; + if (prePathMatrix != NULL) + prePathMatrix->mapRect(&final_rect, path_bounds); + else + final_rect = path_bounds; + + GatherPixelRefDevice::drawRect(draw, final_rect, paint); + } + virtual void drawBitmap(const SkDraw& draw, + const SkBitmap& bitmap, + const SkIRect* srcRectOrNull, + const SkMatrix& matrix, + const SkPaint& paint) SK_OVERRIDE { + SkMatrix total_matrix; + total_matrix.setConcat(*draw.fMatrix, matrix); + + SkRect bitmap_rect = SkRect::MakeWH(bitmap.width(), bitmap.height()); + SkRect mapped_rect; + total_matrix.mapRect(&mapped_rect, bitmap_rect); + addBitmap(bitmap, mapped_rect); + + SkBitmap paint_bitmap; + if (getBitmapFromPaint(paint, &paint_bitmap)) + addBitmap(paint_bitmap, mapped_rect); + } + virtual void drawBitmapRect(const SkDraw& draw, + const SkBitmap& bitmap, + const SkRect* srcOrNull, + const SkRect& dst, + const SkPaint& paint) SK_OVERRIDE { + SkRect bitmap_rect = SkRect::MakeWH(bitmap.width(), bitmap.height()); + SkMatrix matrix; + matrix.setRectToRect(bitmap_rect, dst, SkMatrix::kFill_ScaleToFit); + GatherPixelRefDevice::drawBitmap(draw, bitmap, NULL, matrix, paint); + } + virtual void drawSprite(const SkDraw& draw, + const SkBitmap& bitmap, + int x, + int y, + const SkPaint& paint) SK_OVERRIDE { + // Sprites aren't affected by current matrix, so we can't reuse drawRect. + SkMatrix matrix; + matrix.setTranslate(x, y); + + SkRect bitmap_rect = SkRect::MakeWH(bitmap.width(), bitmap.height()); + SkRect mapped_rect; + matrix.mapRect(&mapped_rect, bitmap_rect); + + addBitmap(bitmap, mapped_rect); + SkBitmap paint_bitmap; + if (getBitmapFromPaint(paint, &paint_bitmap)) + addBitmap(paint_bitmap, mapped_rect); + } + virtual void drawText(const SkDraw& draw, + const void* text, + size_t len, + SkScalar x, + SkScalar y, + const SkPaint& paint) SK_OVERRIDE { + SkBitmap bitmap; + if (!getBitmapFromPaint(paint, &bitmap)) + return; + + // Math is borrowed from SkBBoxRecord + SkRect bounds; + paint.measureText(text, len, &bounds); + SkPaint::FontMetrics metrics; + paint.getFontMetrics(&metrics); + + if (paint.isVerticalText()) { + SkScalar h = bounds.fBottom - bounds.fTop; + if (paint.getTextAlign() == SkPaint::kCenter_Align) { + bounds.fTop -= h / 2; + bounds.fBottom -= h / 2; + } + bounds.fBottom += metrics.fBottom; + bounds.fTop += metrics.fTop; + } + else { + SkScalar w = bounds.fRight - bounds.fLeft; + if (paint.getTextAlign() == SkPaint::kCenter_Align) { + bounds.fLeft -= w / 2; + bounds.fRight -= w / 2; + } else if (paint.getTextAlign() == SkPaint::kRight_Align) { + bounds.fLeft -= w; + bounds.fRight -= w; + } + bounds.fTop = metrics.fTop; + bounds.fBottom = metrics.fBottom; + } + + SkScalar pad = (metrics.fBottom - metrics.fTop) / 2; + bounds.fLeft -= pad; + bounds.fRight += pad; + bounds.fLeft += x; + bounds.fRight += x; + bounds.fTop += y; + bounds.fBottom += y; + + GatherPixelRefDevice::drawRect(draw, bounds, paint); + } + virtual void drawPosText(const SkDraw& draw, + const void* text, + size_t len, + const SkScalar pos[], + SkScalar constY, + int scalarsPerPos, + const SkPaint& paint) SK_OVERRIDE { + SkBitmap bitmap; + if (!getBitmapFromPaint(paint, &bitmap)) + return; + + if (len == 0) + return; + + // Similar to SkDraw asserts. + SkASSERT(scalarsPerPos == 1 || scalarsPerPos == 2); + + SkPoint min_point; + SkPoint max_point; + if (scalarsPerPos == 1) { + min_point.set(pos[0], constY); + max_point.set(pos[0], constY); + } else if (scalarsPerPos == 2) { + min_point.set(pos[0], constY + pos[1]); + max_point.set(pos[0], constY + pos[1]); + } + + for (size_t i = 0; i < len; ++i) { + SkScalar x = pos[i * scalarsPerPos]; + SkScalar y = constY; + if (scalarsPerPos == 2) + y += pos[i * scalarsPerPos + 1]; + + min_point.set( + std::min(x, min_point.x()), + std::min(y, min_point.y())); + max_point.set( + std::max(x, max_point.x()), + std::max(y, max_point.y())); + } + + SkRect bounds = SkRect::MakeLTRB( + min_point.x(), min_point.y(), max_point.x(), max_point.y()); + + // Math is borrowed from SkBBoxRecord + SkPaint::FontMetrics metrics; + paint.getFontMetrics(&metrics); + + bounds.fTop += metrics.fTop; + bounds.fBottom += metrics.fBottom; + + SkScalar pad = (metrics.fTop - metrics.fBottom) / 2; + bounds.fLeft += pad; + bounds.fRight -= pad; + + GatherPixelRefDevice::drawRect(draw, bounds, paint); + } + virtual void drawTextOnPath(const SkDraw& draw, + const void* text, + size_t len, + const SkPath& path, + const SkMatrix* matrix, + const SkPaint& paint) SK_OVERRIDE { + SkBitmap bitmap; + if (!getBitmapFromPaint(paint, &bitmap)) + return; + + // Math is borrowed from SkBBoxRecord + SkRect bounds = path.getBounds(); + SkPaint::FontMetrics metrics; + paint.getFontMetrics(&metrics); + + SkScalar pad = metrics.fTop; + bounds.fLeft += pad; + bounds.fRight -= pad; + bounds.fTop += pad; + bounds.fBottom -= pad; + + GatherPixelRefDevice::drawRect(draw, bounds, paint); + } + virtual void drawVertices(const SkDraw& draw, + 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 { + GatherPixelRefDevice::drawPoints( + draw, SkCanvas::kPolygon_PointMode, vertexCount, verts, paint); + } + virtual void drawDevice(const SkDraw&, + SkDevice*, + int x, + int y, + const SkPaint&) SK_OVERRIDE {} + + protected: + virtual bool onReadPixels(const SkBitmap& bitmap, + int x, + int y, + SkCanvas::Config8888 config8888) SK_OVERRIDE { + return false; + } + + private: + LazyPixelRefSet* lazy_pixel_ref_set_; + + void addBitmap(const SkBitmap& bm, const SkRect& rect) { + lazy_pixel_ref_set_->add(bm.pixelRef(), rect); + } + + bool getBitmapFromPaint(const SkPaint& paint, SkBitmap* bm) { + SkShader* shader = paint.getShader(); + if (shader) { + // Check whether the shader is a gradient in order to prevent generation + // of bitmaps from gradient shaders, which implement asABitmap. + if (SkShader::kNone_GradientType == shader->asAGradient(NULL)) + return shader->asABitmap(bm, NULL, NULL); + } + 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); + } + + virtual bool clipPath(const SkPath& path, SkRegion::Op op, bool doAA) + SK_OVERRIDE { + return this->updateClipConservativelyUsingBounds( + path.getBounds(), op, path.isInverseFillType()); + } + virtual bool clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) + SK_OVERRIDE { + return this->updateClipConservativelyUsingBounds( + rrect.getBounds(), op, false); + } + + private: + typedef SkCanvas INHERITED; +}; + +} // namespace + +void LazyPixelRefUtils::GatherPixelRefs( + SkPicture* picture, + std::vector<PositionLazyPixelRef>* lazy_pixel_refs) { + lazy_pixel_refs->clear(); + LazyPixelRefSet pixel_ref_set(lazy_pixel_refs); + + SkBitmap emptyBitmap; + emptyBitmap.setConfig( + SkBitmap::kNo_Config, picture->width(), picture->height()); + + GatherPixelRefDevice device(emptyBitmap, &pixel_ref_set); + NoSaveLayerCanvas canvas(&device); + + canvas.clipRect(SkRect::MakeWH(picture->width(), picture->height()), + SkRegion::kIntersect_Op, + false); + canvas.drawPicture(*picture); +} + +} // namespace skia |