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.
4
5#include "skia/ext/pixel_ref_utils.h"
6
7#include <algorithm>
8
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/SkPixelRef.h"
14#include "third_party/skia/include/core/SkRRect.h"
15#include "third_party/skia/include/core/SkRect.h"
16#include "third_party/skia/include/core/SkShader.h"
17#include "third_party/skia/include/utils/SkNoSaveLayerCanvas.h"
18#include "third_party/skia/src/core/SkRasterClip.h"
19
20namespace skia {
21
22namespace {
23
24// URI label for a discardable SkPixelRef.
25const char kLabelDiscardable[] = "discardable";
26
27class DiscardablePixelRefSet {
28 public:
29  DiscardablePixelRefSet(
30      std::vector<PixelRefUtils::PositionPixelRef>* pixel_refs)
31      : pixel_refs_(pixel_refs) {}
32
33  void Add(SkPixelRef* pixel_ref, const SkRect& rect) {
34    // Only save discardable pixel refs.
35    if (pixel_ref->getURI() &&
36        !strcmp(pixel_ref->getURI(), kLabelDiscardable)) {
37      PixelRefUtils::PositionPixelRef position_pixel_ref;
38      position_pixel_ref.pixel_ref = pixel_ref;
39      position_pixel_ref.pixel_ref_rect = rect;
40      pixel_refs_->push_back(position_pixel_ref);
41    }
42  }
43
44 private:
45  std::vector<PixelRefUtils::PositionPixelRef>* pixel_refs_;
46};
47
48class GatherPixelRefDevice : public SkBitmapDevice {
49 public:
50  GatherPixelRefDevice(const SkBitmap& bm,
51                       DiscardablePixelRefSet* pixel_ref_set)
52      : SkBitmapDevice(bm), pixel_ref_set_(pixel_ref_set) {}
53
54  virtual void clear(SkColor color) SK_OVERRIDE {}
55  virtual void drawPaint(const SkDraw& draw, const SkPaint& paint) SK_OVERRIDE {
56    SkBitmap bitmap;
57    if (GetBitmapFromPaint(paint, &bitmap)) {
58      SkRect clip_rect = SkRect::Make(draw.fRC->getBounds());
59      AddBitmap(bitmap, clip_rect);
60    }
61  }
62
63  virtual void drawPoints(const SkDraw& draw,
64                          SkCanvas::PointMode mode,
65                          size_t count,
66                          const SkPoint points[],
67                          const SkPaint& paint) SK_OVERRIDE {
68    SkBitmap bitmap;
69    if (!GetBitmapFromPaint(paint, &bitmap))
70      return;
71
72    if (count == 0)
73      return;
74
75    SkPoint min_point = points[0];
76    SkPoint max_point = points[0];
77    for (size_t i = 1; i < count; ++i) {
78      const SkPoint& point = points[i];
79      min_point.set(std::min(min_point.x(), point.x()),
80                    std::min(min_point.y(), point.y()));
81      max_point.set(std::max(max_point.x(), point.x()),
82                    std::max(max_point.y(), point.y()));
83    }
84
85    SkRect bounds = SkRect::MakeLTRB(
86        min_point.x(), min_point.y(), max_point.x(), max_point.y());
87
88    GatherPixelRefDevice::drawRect(draw, bounds, paint);
89  }
90  virtual void drawRect(const SkDraw& draw,
91                        const SkRect& rect,
92                        const SkPaint& paint) SK_OVERRIDE {
93    SkBitmap bitmap;
94    if (GetBitmapFromPaint(paint, &bitmap)) {
95      SkRect mapped_rect;
96      draw.fMatrix->mapRect(&mapped_rect, rect);
97      mapped_rect.intersect(SkRect::Make(draw.fRC->getBounds()));
98      AddBitmap(bitmap, mapped_rect);
99    }
100  }
101  virtual void drawOval(const SkDraw& draw,
102                        const SkRect& rect,
103                        const SkPaint& paint) SK_OVERRIDE {
104    GatherPixelRefDevice::drawRect(draw, rect, paint);
105  }
106  virtual void drawRRect(const SkDraw& draw,
107                         const SkRRect& rect,
108                         const SkPaint& paint) SK_OVERRIDE {
109    GatherPixelRefDevice::drawRect(draw, rect.rect(), paint);
110  }
111  virtual void drawPath(const SkDraw& draw,
112                        const SkPath& path,
113                        const SkPaint& paint,
114                        const SkMatrix* pre_path_matrix,
115                        bool path_is_mutable) SK_OVERRIDE {
116    SkBitmap bitmap;
117    if (!GetBitmapFromPaint(paint, &bitmap))
118      return;
119
120    SkRect path_bounds = path.getBounds();
121    SkRect final_rect;
122    if (pre_path_matrix != NULL)
123      pre_path_matrix->mapRect(&final_rect, path_bounds);
124    else
125      final_rect = path_bounds;
126
127    GatherPixelRefDevice::drawRect(draw, final_rect, paint);
128  }
129  virtual void drawBitmap(const SkDraw& draw,
130                          const SkBitmap& bitmap,
131                          const SkMatrix& matrix,
132                          const SkPaint& paint) SK_OVERRIDE {
133    SkMatrix total_matrix;
134    total_matrix.setConcat(*draw.fMatrix, matrix);
135
136    SkRect bitmap_rect = SkRect::MakeWH(bitmap.width(), bitmap.height());
137    SkRect mapped_rect;
138    total_matrix.mapRect(&mapped_rect, bitmap_rect);
139    AddBitmap(bitmap, mapped_rect);
140
141    SkBitmap paint_bitmap;
142    if (GetBitmapFromPaint(paint, &paint_bitmap))
143      AddBitmap(paint_bitmap, mapped_rect);
144  }
145  virtual void drawBitmapRect(const SkDraw& draw,
146                              const SkBitmap& bitmap,
147                              const SkRect* src_or_null,
148                              const SkRect& dst,
149                              const SkPaint& paint,
150                              SkCanvas::DrawBitmapRectFlags flags) SK_OVERRIDE {
151    SkRect bitmap_rect = SkRect::MakeWH(bitmap.width(), bitmap.height());
152    SkMatrix matrix;
153    matrix.setRectToRect(bitmap_rect, dst, SkMatrix::kFill_ScaleToFit);
154    GatherPixelRefDevice::drawBitmap(draw, bitmap, matrix, paint);
155  }
156  virtual void drawSprite(const SkDraw& draw,
157                          const SkBitmap& bitmap,
158                          int x,
159                          int y,
160                          const SkPaint& paint) SK_OVERRIDE {
161    // Sprites aren't affected by current matrix, so we can't reuse drawRect.
162    SkMatrix matrix;
163    matrix.setTranslate(x, y);
164
165    SkRect bitmap_rect = SkRect::MakeWH(bitmap.width(), bitmap.height());
166    SkRect mapped_rect;
167    matrix.mapRect(&mapped_rect, bitmap_rect);
168
169    AddBitmap(bitmap, mapped_rect);
170    SkBitmap paint_bitmap;
171    if (GetBitmapFromPaint(paint, &paint_bitmap))
172      AddBitmap(paint_bitmap, mapped_rect);
173  }
174  virtual void drawText(const SkDraw& draw,
175                        const void* text,
176                        size_t len,
177                        SkScalar x,
178                        SkScalar y,
179                        const SkPaint& paint) SK_OVERRIDE {
180    SkBitmap bitmap;
181    if (!GetBitmapFromPaint(paint, &bitmap))
182      return;
183
184    // Math is borrowed from SkBBoxRecord
185    SkRect bounds;
186    paint.measureText(text, len, &bounds);
187    SkPaint::FontMetrics metrics;
188    paint.getFontMetrics(&metrics);
189
190    if (paint.isVerticalText()) {
191      SkScalar h = bounds.fBottom - bounds.fTop;
192      if (paint.getTextAlign() == SkPaint::kCenter_Align) {
193        bounds.fTop -= h / 2;
194        bounds.fBottom -= h / 2;
195      }
196      bounds.fBottom += metrics.fBottom;
197      bounds.fTop += metrics.fTop;
198    } else {
199      SkScalar w = bounds.fRight - bounds.fLeft;
200      if (paint.getTextAlign() == SkPaint::kCenter_Align) {
201        bounds.fLeft -= w / 2;
202        bounds.fRight -= w / 2;
203      } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
204        bounds.fLeft -= w;
205        bounds.fRight -= w;
206      }
207      bounds.fTop = metrics.fTop;
208      bounds.fBottom = metrics.fBottom;
209    }
210
211    SkScalar pad = (metrics.fBottom - metrics.fTop) / 2;
212    bounds.fLeft -= pad;
213    bounds.fRight += pad;
214    bounds.fLeft += x;
215    bounds.fRight += x;
216    bounds.fTop += y;
217    bounds.fBottom += y;
218
219    GatherPixelRefDevice::drawRect(draw, bounds, paint);
220  }
221  virtual void drawPosText(const SkDraw& draw,
222                           const void* text,
223                           size_t len,
224                           const SkScalar pos[],
225                           SkScalar const_y,
226                           int scalars_per_pos,
227                           const SkPaint& paint) SK_OVERRIDE {
228    SkBitmap bitmap;
229    if (!GetBitmapFromPaint(paint, &bitmap))
230      return;
231
232    if (len == 0)
233      return;
234
235    // Similar to SkDraw asserts.
236    SkASSERT(scalars_per_pos == 1 || scalars_per_pos == 2);
237
238    SkPoint min_point;
239    SkPoint max_point;
240    if (scalars_per_pos == 1) {
241      min_point.set(pos[0], const_y);
242      max_point.set(pos[0], const_y);
243    } else if (scalars_per_pos == 2) {
244      min_point.set(pos[0], const_y + pos[1]);
245      max_point.set(pos[0], const_y + pos[1]);
246    }
247
248    for (size_t i = 0; i < len; ++i) {
249      SkScalar x = pos[i * scalars_per_pos];
250      SkScalar y = const_y;
251      if (scalars_per_pos == 2)
252        y += pos[i * scalars_per_pos + 1];
253
254      min_point.set(std::min(x, min_point.x()), std::min(y, min_point.y()));
255      max_point.set(std::max(x, max_point.x()), std::max(y, max_point.y()));
256    }
257
258    SkRect bounds = SkRect::MakeLTRB(
259        min_point.x(), min_point.y(), max_point.x(), max_point.y());
260
261    // Math is borrowed from SkBBoxRecord
262    SkPaint::FontMetrics metrics;
263    paint.getFontMetrics(&metrics);
264
265    bounds.fTop += metrics.fTop;
266    bounds.fBottom += metrics.fBottom;
267
268    SkScalar pad = (metrics.fTop - metrics.fBottom) / 2;
269    bounds.fLeft += pad;
270    bounds.fRight -= pad;
271
272    GatherPixelRefDevice::drawRect(draw, bounds, paint);
273  }
274  virtual void drawTextOnPath(const SkDraw& draw,
275                              const void* text,
276                              size_t len,
277                              const SkPath& path,
278                              const SkMatrix* matrix,
279                              const SkPaint& paint) SK_OVERRIDE {
280    SkBitmap bitmap;
281    if (!GetBitmapFromPaint(paint, &bitmap))
282      return;
283
284    // Math is borrowed from SkBBoxRecord
285    SkRect bounds = path.getBounds();
286    SkPaint::FontMetrics metrics;
287    paint.getFontMetrics(&metrics);
288
289    SkScalar pad = metrics.fTop;
290    bounds.fLeft += pad;
291    bounds.fRight -= pad;
292    bounds.fTop += pad;
293    bounds.fBottom -= pad;
294
295    GatherPixelRefDevice::drawRect(draw, bounds, paint);
296  }
297  virtual void drawVertices(const SkDraw& draw,
298                            SkCanvas::VertexMode,
299                            int vertex_count,
300                            const SkPoint verts[],
301                            const SkPoint texs[],
302                            const SkColor colors[],
303                            SkXfermode* xmode,
304                            const uint16_t indices[],
305                            int index_count,
306                            const SkPaint& paint) SK_OVERRIDE {
307    GatherPixelRefDevice::drawPoints(
308        draw, SkCanvas::kPolygon_PointMode, vertex_count, verts, paint);
309  }
310  virtual void drawDevice(const SkDraw&,
311                          SkBaseDevice*,
312                          int x,
313                          int y,
314                          const SkPaint&) SK_OVERRIDE {}
315
316 protected:
317  virtual bool onReadPixels(const SkImageInfo& info,
318                            void* pixels,
319                            size_t rowBytes,
320                            int x,
321                            int y) SK_OVERRIDE {
322    return false;
323  }
324
325  virtual bool onWritePixels(const SkImageInfo& info,
326                             const void* pixels,
327                             size_t rowBytes,
328                             int x,
329                             int y) SK_OVERRIDE {
330    return false;
331  }
332
333 private:
334  DiscardablePixelRefSet* pixel_ref_set_;
335
336  void AddBitmap(const SkBitmap& bm, const SkRect& rect) {
337    SkRect canvas_rect = SkRect::MakeWH(width(), height());
338    SkRect paint_rect = SkRect::MakeEmpty();
339    paint_rect.intersect(rect, canvas_rect);
340    pixel_ref_set_->Add(bm.pixelRef(), paint_rect);
341  }
342
343  bool GetBitmapFromPaint(const SkPaint& paint, SkBitmap* bm) {
344    SkShader* shader = paint.getShader();
345    if (shader) {
346      // Check whether the shader is a gradient in order to prevent generation
347      // of bitmaps from gradient shaders, which implement asABitmap.
348      if (SkShader::kNone_GradientType == shader->asAGradient(NULL))
349        return shader->asABitmap(bm, NULL, NULL);
350    }
351    return false;
352  }
353};
354
355}  // namespace
356
357void PixelRefUtils::GatherDiscardablePixelRefs(
358    SkPicture* picture,
359    std::vector<PositionPixelRef>* pixel_refs) {
360  pixel_refs->clear();
361  DiscardablePixelRefSet pixel_ref_set(pixel_refs);
362
363  SkBitmap empty_bitmap;
364  empty_bitmap.setInfo(SkImageInfo::MakeUnknown(picture->width(), picture->height()));
365
366  GatherPixelRefDevice device(empty_bitmap, &pixel_ref_set);
367  SkNoSaveLayerCanvas canvas(&device);
368
369  canvas.clipRect(SkRect::MakeWH(picture->width(), picture->height()),
370                  SkRegion::kIntersect_Op,
371                  false);
372  canvas.drawPicture(picture);
373}
374
375}  // namespace skia
376