// 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