| // 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) {} |
| |
| void drawPaint(const SkDraw& draw, const SkPaint& paint) override { |
| SkBitmap bitmap; |
| if (GetBitmapFromPaint(paint, &bitmap)) { |
| SkRect clip_rect = SkRect::Make(draw.fRC->getBounds()); |
| AddBitmap(bitmap, clip_rect); |
| } |
| } |
| |
| void drawPoints(const SkDraw& draw, |
| SkCanvas::PointMode mode, |
| size_t count, |
| const SkPoint points[], |
| const SkPaint& paint) 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); |
| } |
| void drawRect(const SkDraw& draw, |
| const SkRect& rect, |
| const SkPaint& paint) override { |
| SkBitmap bitmap; |
| if (GetBitmapFromPaint(paint, &bitmap)) { |
| SkRect mapped_rect; |
| draw.fMatrix->mapRect(&mapped_rect, rect); |
| if (mapped_rect.intersect(SkRect::Make(draw.fRC->getBounds()))) |
| AddBitmap(bitmap, mapped_rect); |
| } |
| } |
| void drawOval(const SkDraw& draw, |
| const SkRect& rect, |
| const SkPaint& paint) override { |
| GatherPixelRefDevice::drawRect(draw, rect, paint); |
| } |
| void drawRRect(const SkDraw& draw, |
| const SkRRect& rect, |
| const SkPaint& paint) override { |
| GatherPixelRefDevice::drawRect(draw, rect.rect(), paint); |
| } |
| void drawPath(const SkDraw& draw, |
| const SkPath& path, |
| const SkPaint& paint, |
| const SkMatrix* pre_path_matrix, |
| bool path_is_mutable) 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); |
| } |
| void drawBitmap(const SkDraw& draw, |
| const SkBitmap& bitmap, |
| const SkMatrix& matrix, |
| const SkPaint& paint) 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); |
| } |
| void drawBitmapRect(const SkDraw& draw, |
| const SkBitmap& bitmap, |
| const SkRect* src_or_null, |
| const SkRect& dst, |
| const SkPaint& paint, |
| SkCanvas::SrcRectConstraint flags) 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); |
| } |
| void drawSprite(const SkDraw& draw, |
| const SkBitmap& bitmap, |
| int x, |
| int y, |
| const SkPaint& paint) 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); |
| } |
| void drawText(const SkDraw& draw, |
| const void* text, |
| size_t len, |
| SkScalar x, |
| SkScalar y, |
| const SkPaint& paint) 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); |
| } |
| void drawPosText(const SkDraw& draw, |
| const void* text, |
| size_t len, |
| const SkScalar pos[], |
| int scalars_per_pos, |
| const SkPoint& offset, |
| const SkPaint& paint) 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::Make(offset.x() + pos[0], |
| offset.y() + (2 == scalars_per_pos ? pos[1] : 0)); |
| SkPoint max_point = min_point; |
| |
| for (size_t i = 0; i < len; ++i) { |
| SkScalar x = offset.x() + pos[i * scalars_per_pos]; |
| SkScalar y = offset.y() + (2 == scalars_per_pos ? pos[i * scalars_per_pos + 1] : 0); |
| |
| 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); |
| } |
| void drawTextOnPath(const SkDraw& draw, |
| const void* text, |
| size_t len, |
| const SkPath& path, |
| const SkMatrix* matrix, |
| const SkPaint& paint) 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); |
| } |
| 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) override { |
| GatherPixelRefDevice::drawPoints( |
| draw, SkCanvas::kPolygon_PointMode, vertex_count, verts, paint); |
| } |
| void drawDevice(const SkDraw&, |
| SkBaseDevice*, |
| int x, |
| int y, |
| const SkPaint&) override {} |
| |
| protected: |
| bool onReadPixels(const SkImageInfo& info, |
| void* pixels, |
| size_t rowBytes, |
| int x, |
| int y) override { |
| return false; |
| } |
| |
| bool onWritePixels(const SkImageInfo& info, |
| const void* pixels, |
| size_t rowBytes, |
| int x, |
| int y) 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(); |
| if (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); |
| |
| SkRect picture_bounds = picture->cullRect(); |
| SkIRect picture_ibounds = picture_bounds.roundOut(); |
| SkBitmap empty_bitmap; |
| empty_bitmap.setInfo(SkImageInfo::MakeUnknown(picture_ibounds.width(), |
| picture_ibounds.height())); |
| |
| GatherPixelRefDevice device(empty_bitmap, &pixel_ref_set); |
| SkNoSaveLayerCanvas canvas(&device); |
| |
| // Draw the picture pinned against our top/left corner. |
| canvas.translate(-picture_bounds.left(), -picture_bounds.top()); |
| canvas.drawPicture(picture); |
| } |
| |
| } // namespace skia |