mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
370 lines
12 KiB
C++
370 lines
12 KiB
C++
// 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/SkPath.h"
|
|
#include "third_party/skia/include/core/SkPixelRef.h"
|
|
#include "third_party/skia/include/core/SkRect.h"
|
|
#include "third_party/skia/include/core/SkRRect.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->isABitmap(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
|