1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "skia/ext/pixel_ref_utils.h"
9 #include "third_party/skia/include/core/SkBitmapDevice.h"
10 #include "third_party/skia/include/core/SkCanvas.h"
11 #include "third_party/skia/include/core/SkData.h"
12 #include "third_party/skia/include/core/SkDraw.h"
13 #include "third_party/skia/include/core/SkPath.h"
14 #include "third_party/skia/include/core/SkPixelRef.h"
15 #include "third_party/skia/include/core/SkRRect.h"
16 #include "third_party/skia/include/core/SkRect.h"
17 #include "third_party/skia/include/core/SkShader.h"
18 #include "third_party/skia/include/utils/SkNoSaveLayerCanvas.h"
19 #include "third_party/skia/src/core/SkRasterClip.h"
25 // URI label for a discardable SkPixelRef.
26 const char kLabelDiscardable
[] = "discardable";
28 class DiscardablePixelRefSet
{
30 DiscardablePixelRefSet(
31 std::vector
<PixelRefUtils::PositionPixelRef
>* pixel_refs
)
32 : pixel_refs_(pixel_refs
) {}
34 void Add(SkPixelRef
* pixel_ref
,
36 const SkMatrix
& matrix
,
37 SkFilterQuality filter_quality
) {
38 // Only save discardable pixel refs.
39 if (pixel_ref
->getURI() &&
40 !strcmp(pixel_ref
->getURI(), kLabelDiscardable
)) {
41 PixelRefUtils::PositionPixelRef position_pixel_ref
;
42 position_pixel_ref
.pixel_ref
= pixel_ref
;
43 position_pixel_ref
.pixel_ref_rect
= rect
;
44 position_pixel_ref
.matrix
= matrix
;
45 position_pixel_ref
.filter_quality
= filter_quality
;
46 pixel_refs_
->push_back(position_pixel_ref
);
51 std::vector
<PixelRefUtils::PositionPixelRef
>* pixel_refs_
;
54 class GatherPixelRefDevice
: public SkBitmapDevice
{
56 GatherPixelRefDevice(const SkBitmap
& bm
,
57 DiscardablePixelRefSet
* pixel_ref_set
)
58 : SkBitmapDevice(bm
), pixel_ref_set_(pixel_ref_set
) {}
60 void drawPaint(const SkDraw
& draw
, const SkPaint
& paint
) override
{
62 if (GetBitmapFromPaint(paint
, &bitmap
)) {
63 SkRect clip_rect
= SkRect::Make(draw
.fRC
->getBounds());
64 AddBitmap(bitmap
, clip_rect
, *draw
.fMatrix
, paint
.getFilterQuality());
68 void drawPoints(const SkDraw
& draw
,
69 SkCanvas::PointMode mode
,
71 const SkPoint points
[],
72 const SkPaint
& paint
) override
{
74 if (!GetBitmapFromPaint(paint
, &bitmap
))
80 SkPoint min_point
= points
[0];
81 SkPoint max_point
= points
[0];
82 for (size_t i
= 1; i
< count
; ++i
) {
83 const SkPoint
& point
= points
[i
];
84 min_point
.set(std::min(min_point
.x(), point
.x()),
85 std::min(min_point
.y(), point
.y()));
86 max_point
.set(std::max(max_point
.x(), point
.x()),
87 std::max(max_point
.y(), point
.y()));
90 SkRect bounds
= SkRect::MakeLTRB(
91 min_point
.x(), min_point
.y(), max_point
.x(), max_point
.y());
93 GatherPixelRefDevice::drawRect(draw
, bounds
, paint
);
95 void drawRect(const SkDraw
& draw
,
97 const SkPaint
& paint
) override
{
99 if (GetBitmapFromPaint(paint
, &bitmap
)) {
101 draw
.fMatrix
->mapRect(&mapped_rect
, rect
);
102 if (mapped_rect
.intersect(SkRect::Make(draw
.fRC
->getBounds()))) {
103 AddBitmap(bitmap
, mapped_rect
, *draw
.fMatrix
, paint
.getFilterQuality());
107 void drawOval(const SkDraw
& draw
,
109 const SkPaint
& paint
) override
{
110 GatherPixelRefDevice::drawRect(draw
, rect
, paint
);
112 void drawRRect(const SkDraw
& draw
,
114 const SkPaint
& paint
) override
{
115 GatherPixelRefDevice::drawRect(draw
, rect
.rect(), paint
);
117 void drawPath(const SkDraw
& draw
,
119 const SkPaint
& paint
,
120 const SkMatrix
* pre_path_matrix
,
121 bool path_is_mutable
) override
{
123 if (!GetBitmapFromPaint(paint
, &bitmap
))
126 SkRect path_bounds
= path
.getBounds();
128 if (pre_path_matrix
!= NULL
)
129 pre_path_matrix
->mapRect(&final_rect
, path_bounds
);
131 final_rect
= path_bounds
;
133 GatherPixelRefDevice::drawRect(draw
, final_rect
, paint
);
135 void drawBitmap(const SkDraw
& draw
,
136 const SkBitmap
& bitmap
,
137 const SkMatrix
& matrix
,
138 const SkPaint
& paint
) override
{
139 SkMatrix total_matrix
;
140 total_matrix
.setConcat(*draw
.fMatrix
, matrix
);
142 SkRect bitmap_rect
= SkRect::MakeWH(bitmap
.width(), bitmap
.height());
144 total_matrix
.mapRect(&mapped_rect
, bitmap_rect
);
145 AddBitmap(bitmap
, mapped_rect
, total_matrix
, paint
.getFilterQuality());
147 SkBitmap paint_bitmap
;
148 if (GetBitmapFromPaint(paint
, &paint_bitmap
)) {
149 AddBitmap(paint_bitmap
, mapped_rect
, total_matrix
,
150 paint
.getFilterQuality());
153 void drawBitmapRect(const SkDraw
& draw
,
154 const SkBitmap
& bitmap
,
155 const SkRect
* src_or_null
,
157 const SkPaint
& paint
,
158 SkCanvas::SrcRectConstraint
) override
{
159 SkRect bitmap_rect
= SkRect::MakeWH(bitmap
.width(), bitmap
.height());
161 matrix
.setRectToRect(bitmap_rect
, dst
, SkMatrix::kFill_ScaleToFit
);
162 GatherPixelRefDevice::drawBitmap(draw
, bitmap
, matrix
, paint
);
164 void drawSprite(const SkDraw
& draw
,
165 const SkBitmap
& bitmap
,
168 const SkPaint
& paint
) override
{
169 // Sprites aren't affected by current matrix, so we can't reuse drawRect.
171 matrix
.setTranslate(x
, y
);
173 SkRect bitmap_rect
= SkRect::MakeWH(bitmap
.width(), bitmap
.height());
175 matrix
.mapRect(&mapped_rect
, bitmap_rect
);
178 identity
.setIdentity();
179 // Sprites aren't affected by current matrix, so use the identity matrix.
180 AddBitmap(bitmap
, mapped_rect
, identity
, paint
.getFilterQuality());
181 SkBitmap paint_bitmap
;
182 if (GetBitmapFromPaint(paint
, &paint_bitmap
))
183 AddBitmap(paint_bitmap
, mapped_rect
, identity
, paint
.getFilterQuality());
185 void drawText(const SkDraw
& draw
,
190 const SkPaint
& paint
) override
{
192 if (!GetBitmapFromPaint(paint
, &bitmap
))
195 // Math is borrowed from SkBBoxRecord
197 paint
.measureText(text
, len
, &bounds
);
198 SkPaint::FontMetrics metrics
;
199 paint
.getFontMetrics(&metrics
);
201 if (paint
.isVerticalText()) {
202 SkScalar h
= bounds
.fBottom
- bounds
.fTop
;
203 if (paint
.getTextAlign() == SkPaint::kCenter_Align
) {
204 bounds
.fTop
-= h
/ 2;
205 bounds
.fBottom
-= h
/ 2;
207 bounds
.fBottom
+= metrics
.fBottom
;
208 bounds
.fTop
+= metrics
.fTop
;
210 SkScalar w
= bounds
.fRight
- bounds
.fLeft
;
211 if (paint
.getTextAlign() == SkPaint::kCenter_Align
) {
212 bounds
.fLeft
-= w
/ 2;
213 bounds
.fRight
-= w
/ 2;
214 } else if (paint
.getTextAlign() == SkPaint::kRight_Align
) {
218 bounds
.fTop
= metrics
.fTop
;
219 bounds
.fBottom
= metrics
.fBottom
;
222 SkScalar pad
= (metrics
.fBottom
- metrics
.fTop
) / 2;
224 bounds
.fRight
+= pad
;
230 GatherPixelRefDevice::drawRect(draw
, bounds
, paint
);
232 void drawPosText(const SkDraw
& draw
,
235 const SkScalar pos
[],
237 const SkPoint
& offset
,
238 const SkPaint
& paint
) override
{
240 if (!GetBitmapFromPaint(paint
, &bitmap
))
246 // Similar to SkDraw asserts.
247 SkASSERT(scalars_per_pos
== 1 || scalars_per_pos
== 2);
249 SkPoint min_point
= SkPoint::Make(offset
.x() + pos
[0],
250 offset
.y() + (2 == scalars_per_pos
? pos
[1] : 0));
251 SkPoint max_point
= min_point
;
253 for (size_t i
= 0; i
< len
; ++i
) {
254 SkScalar x
= offset
.x() + pos
[i
* scalars_per_pos
];
255 SkScalar y
= offset
.y() + (2 == scalars_per_pos
? pos
[i
* scalars_per_pos
+ 1] : 0);
257 min_point
.set(std::min(x
, min_point
.x()), std::min(y
, min_point
.y()));
258 max_point
.set(std::max(x
, max_point
.x()), std::max(y
, max_point
.y()));
261 SkRect bounds
= SkRect::MakeLTRB(
262 min_point
.x(), min_point
.y(), max_point
.x(), max_point
.y());
264 // Math is borrowed from SkBBoxRecord
265 SkPaint::FontMetrics metrics
;
266 paint
.getFontMetrics(&metrics
);
268 bounds
.fTop
+= metrics
.fTop
;
269 bounds
.fBottom
+= metrics
.fBottom
;
271 SkScalar pad
= (metrics
.fTop
- metrics
.fBottom
) / 2;
273 bounds
.fRight
-= pad
;
275 GatherPixelRefDevice::drawRect(draw
, bounds
, paint
);
277 void drawTextOnPath(const SkDraw
& draw
,
281 const SkMatrix
* matrix
,
282 const SkPaint
& paint
) override
{
284 if (!GetBitmapFromPaint(paint
, &bitmap
))
287 // Math is borrowed from SkBBoxRecord
288 SkRect bounds
= path
.getBounds();
289 SkPaint::FontMetrics metrics
;
290 paint
.getFontMetrics(&metrics
);
292 SkScalar pad
= metrics
.fTop
;
294 bounds
.fRight
-= pad
;
296 bounds
.fBottom
-= pad
;
298 GatherPixelRefDevice::drawRect(draw
, bounds
, paint
);
300 void drawVertices(const SkDraw
& draw
,
301 SkCanvas::VertexMode
,
303 const SkPoint verts
[],
304 const SkPoint texs
[],
305 const SkColor colors
[],
307 const uint16_t indices
[],
309 const SkPaint
& paint
) override
{
310 GatherPixelRefDevice::drawPoints(
311 draw
, SkCanvas::kPolygon_PointMode
, vertex_count
, verts
, paint
);
313 void drawDevice(const SkDraw
&,
317 const SkPaint
&) override
{}
320 bool onReadPixels(const SkImageInfo
& info
,
328 bool onWritePixels(const SkImageInfo
& info
,
337 DiscardablePixelRefSet
* pixel_ref_set_
;
339 void AddBitmap(const SkBitmap
& bm
,
341 const SkMatrix
& matrix
,
342 SkFilterQuality filter_quality
) {
343 SkRect canvas_rect
= SkRect::MakeWH(width(), height());
344 SkRect paint_rect
= SkRect::MakeEmpty();
345 if (paint_rect
.intersect(rect
, canvas_rect
)) {
346 pixel_ref_set_
->Add(bm
.pixelRef(), paint_rect
, matrix
,
351 bool GetBitmapFromPaint(const SkPaint
& paint
, SkBitmap
* bm
) {
352 SkShader
* shader
= paint
.getShader();
354 // Check whether the shader is a gradient in order to prevent generation
355 // of bitmaps from gradient shaders, which implement asABitmap.
356 if (SkShader::kNone_GradientType
== shader
->asAGradient(NULL
))
357 return shader
->asABitmap(bm
, NULL
, NULL
);
365 void PixelRefUtils::GatherDiscardablePixelRefs(
367 std::vector
<PositionPixelRef
>* pixel_refs
) {
369 DiscardablePixelRefSet
pixel_ref_set(pixel_refs
);
371 SkRect picture_bounds
= picture
->cullRect();
372 SkIRect picture_ibounds
= picture_bounds
.roundOut();
373 SkBitmap empty_bitmap
;
374 empty_bitmap
.setInfo(SkImageInfo::MakeUnknown(picture_ibounds
.width(),
375 picture_ibounds
.height()));
377 GatherPixelRefDevice
device(empty_bitmap
, &pixel_ref_set
);
378 SkNoSaveLayerCanvas
canvas(&device
);
380 // Draw the picture pinned against our top/left corner.
381 canvas
.translate(-picture_bounds
.left(), -picture_bounds
.top());
382 canvas
.drawPicture(picture
);