// Copyright 2014 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 "skia/ext/pixel_ref_utils.h" #include <algorithm> #include "third_party/skia/include/core/SkBitmapDevice.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkData.h" #include "third_party/skia/include/core/SkDraw.h" #include "third_party/skia/include/core/SkPixelRef.h" #include "third_party/skia/include/core/SkRRect.h" #include "third_party/skia/include/core/SkRect.h" #include "third_party/skia/include/core/SkShader.h" #include "third_party/skia/include/utils/SkNoSaveLayerCanvas.h" #include "third_party/skia/src/core/SkRasterClip.h" namespace skia { namespace { // URI label for a discardable SkPixelRef. const char kLabelDiscardable[] = "discardable"; class DiscardablePixelRefSet { public: DiscardablePixelRefSet( std::vector<PixelRefUtils::PositionPixelRef>* pixel_refs) : pixel_refs_(pixel_refs) {} void Add(SkPixelRef* pixel_ref, const SkRect& rect) { // Only save discardable pixel refs. if (pixel_ref->getURI() && !strcmp(pixel_ref->getURI(), kLabelDiscardable)) { PixelRefUtils::PositionPixelRef position_pixel_ref; position_pixel_ref.pixel_ref = pixel_ref; position_pixel_ref.pixel_ref_rect = rect; pixel_refs_->push_back(position_pixel_ref); } } private: std::vector<PixelRefUtils::PositionPixelRef>* pixel_refs_; }; class GatherPixelRefDevice : public SkBitmapDevice { public: GatherPixelRefDevice(const SkBitmap& bm, DiscardablePixelRefSet* pixel_ref_set) : SkBitmapDevice(bm), pixel_ref_set_(pixel_ref_set) {} virtual void clear(SkColor color) 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()); AddBitmap(bitmap, clip_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* pre_path_matrix, bool path_is_mutable) SK_OVERRIDE { SkBitmap bitmap; if (!GetBitmapFromPaint(paint, &bitmap)) return; SkRect path_bounds = path.getBounds(); SkRect final_rect; if (pre_path_matrix != NULL) pre_path_matrix->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 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* src_or_null, const SkRect& dst, const SkPaint& paint, SkCanvas::DrawBitmapRectFlags flags) 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, 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 const_y, int scalars_per_pos, const SkPaint& paint) SK_OVERRIDE { SkBitmap bitmap; if (!GetBitmapFromPaint(paint, &bitmap)) return; if (len == 0) return; // Similar to SkDraw asserts. SkASSERT(scalars_per_pos == 1 || scalars_per_pos == 2); SkPoint min_point; SkPoint max_point; if (scalars_per_pos == 1) { min_point.set(pos[0], const_y); max_point.set(pos[0], const_y); } else if (scalars_per_pos == 2) { min_point.set(pos[0], const_y + pos[1]); max_point.set(pos[0], const_y + pos[1]); } for (size_t i = 0; i < len; ++i) { SkScalar x = pos[i * scalars_per_pos]; SkScalar y = const_y; if (scalars_per_pos == 2) y += pos[i * scalars_per_pos + 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 vertex_count, const SkPoint verts[], const SkPoint texs[], const SkColor colors[], SkXfermode* xmode, const uint16_t indices[], int index_count, const SkPaint& paint) SK_OVERRIDE { GatherPixelRefDevice::drawPoints( draw, SkCanvas::kPolygon_PointMode, vertex_count, verts, paint); } virtual void drawDevice(const SkDraw&, SkBaseDevice*, int x, int y, const SkPaint&) SK_OVERRIDE {} protected: virtual bool onReadPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, int x, int y) SK_OVERRIDE { return false; } virtual bool onWritePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes, int x, int y) SK_OVERRIDE { return false; } private: DiscardablePixelRefSet* pixel_ref_set_; void AddBitmap(const SkBitmap& bm, const SkRect& rect) { SkRect canvas_rect = SkRect::MakeWH(width(), height()); SkRect paint_rect = SkRect::MakeEmpty(); paint_rect.intersect(rect, canvas_rect); pixel_ref_set_->Add(bm.pixelRef(), paint_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; } }; } // namespace void PixelRefUtils::GatherDiscardablePixelRefs( SkPicture* picture, std::vector<PositionPixelRef>* pixel_refs) { pixel_refs->clear(); DiscardablePixelRefSet pixel_ref_set(pixel_refs); SkBitmap empty_bitmap; empty_bitmap.setInfo(SkImageInfo::MakeUnknown(picture->width(), picture->height())); GatherPixelRefDevice device(empty_bitmap, &pixel_ref_set); SkNoSaveLayerCanvas canvas(&device); canvas.clipRect(SkRect::MakeWH(picture->width(), picture->height()), SkRegion::kIntersect_Op, false); canvas.drawPicture(picture); } } // namespace skia