blob: b1443e61770bb86447f10ee2f56c2b1614022127 [file] [log] [blame]
// 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 "cc/raster/raster_source.h"
#include <stddef.h>
#include "base/trace_event/trace_event.h"
#include "cc/base/math_util.h"
#include "cc/base/region.h"
#include "cc/debug/debug_colors.h"
#include "cc/debug/traced_value.h"
#include "cc/paint/display_item_list.h"
#include "cc/raster/image_hijack_canvas.h"
#include "cc/raster/skip_image_canvas.h"
#include "skia/ext/analysis_canvas.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkColorSpaceXformCanvas.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
#include "ui/gfx/geometry/axis_transform2d.h"
#include "ui/gfx/geometry/rect_conversions.h"
namespace cc {
scoped_refptr<RasterSource> RasterSource::CreateFromRecordingSource(
const RecordingSource* other,
bool can_use_lcd_text) {
return make_scoped_refptr(new RasterSource(other, can_use_lcd_text));
}
RasterSource::RasterSource(const RecordingSource* other, bool can_use_lcd_text)
: display_list_(other->display_list_),
painter_reported_memory_usage_(other->painter_reported_memory_usage_),
background_color_(other->background_color_),
requires_clear_(other->requires_clear_),
can_use_lcd_text_(can_use_lcd_text),
is_solid_color_(other->is_solid_color_),
solid_color_(other->solid_color_),
recorded_viewport_(other->recorded_viewport_),
size_(other->size_),
clear_canvas_with_debug_color_(other->clear_canvas_with_debug_color_),
slow_down_raster_scale_factor_for_debug_(
other->slow_down_raster_scale_factor_for_debug_),
image_decode_cache_(nullptr) {}
RasterSource::RasterSource(const RasterSource* other, bool can_use_lcd_text)
: display_list_(other->display_list_),
painter_reported_memory_usage_(other->painter_reported_memory_usage_),
background_color_(other->background_color_),
requires_clear_(other->requires_clear_),
can_use_lcd_text_(can_use_lcd_text),
is_solid_color_(other->is_solid_color_),
solid_color_(other->solid_color_),
recorded_viewport_(other->recorded_viewport_),
size_(other->size_),
clear_canvas_with_debug_color_(other->clear_canvas_with_debug_color_),
slow_down_raster_scale_factor_for_debug_(
other->slow_down_raster_scale_factor_for_debug_),
image_decode_cache_(other->image_decode_cache_) {}
RasterSource::~RasterSource() {}
void RasterSource::PlaybackToCanvas(
SkCanvas* raster_canvas,
const gfx::ColorSpace& target_color_space,
const gfx::Rect& canvas_bitmap_rect,
const gfx::Rect& canvas_playback_rect,
const gfx::AxisTransform2d& raster_transform,
const PlaybackSettings& settings) const {
SkIRect raster_bounds = gfx::RectToSkIRect(canvas_bitmap_rect);
if (!canvas_playback_rect.IsEmpty() &&
!raster_bounds.intersect(gfx::RectToSkIRect(canvas_playback_rect)))
return;
// Treat all subnormal values as zero for performance.
ScopedSubnormalFloatDisabler disabler;
raster_canvas->save();
raster_canvas->translate(-canvas_bitmap_rect.x(), -canvas_bitmap_rect.y());
raster_canvas->clipRect(SkRect::MakeFromIRect(raster_bounds));
raster_canvas->translate(raster_transform.translation().x(),
raster_transform.translation().y());
raster_canvas->scale(raster_transform.scale(), raster_transform.scale());
PlaybackToCanvas(raster_canvas, target_color_space, settings);
raster_canvas->restore();
}
void RasterSource::PlaybackToCanvas(SkCanvas* input_canvas,
const gfx::ColorSpace& target_color_space,
const PlaybackSettings& settings) const {
SkCanvas* raster_canvas = input_canvas;
std::unique_ptr<SkCanvas> color_transform_canvas;
if (target_color_space.IsValid()) {
color_transform_canvas = SkCreateColorSpaceXformCanvas(
input_canvas, target_color_space.ToSkColorSpace());
raster_canvas = color_transform_canvas.get();
}
if (!settings.playback_to_shared_canvas)
PrepareForPlaybackToCanvas(raster_canvas);
if (settings.skip_images) {
SkipImageCanvas canvas(raster_canvas);
RasterCommon(&canvas, nullptr);
} else if (settings.use_image_hijack_canvas) {
const SkImageInfo& info = raster_canvas->imageInfo();
ImageHijackCanvas canvas(info.width(), info.height(), image_decode_cache_,
&settings.images_to_skip, target_color_space);
// Before adding the canvas, make sure that the ImageHijackCanvas is aware
// of the current transform and clip, which may affect the clip bounds.
// Since we query the clip bounds of the current canvas to get the list of
// draw commands to process, this is important to produce correct content.
canvas.clipRect(
SkRect::MakeFromIRect(raster_canvas->getDeviceClipBounds()));
canvas.setMatrix(raster_canvas->getTotalMatrix());
canvas.addCanvas(raster_canvas);
RasterCommon(&canvas, nullptr);
} else {
RasterCommon(raster_canvas, nullptr);
}
}
namespace {
bool CanvasIsUnclipped(const SkCanvas* canvas) {
if (!canvas->isClipRect())
return false;
SkIRect bounds;
if (!canvas->getDeviceClipBounds(&bounds))
return false;
SkISize size = canvas->getBaseLayerSize();
return bounds.contains(0, 0, size.width(), size.height());
}
} // namespace
void RasterSource::PrepareForPlaybackToCanvas(SkCanvas* canvas) const {
// TODO(hendrikw): See if we can split this up into separate functions.
if (CanvasIsUnclipped(canvas))
canvas->discard();
// If this raster source has opaque contents, it is guaranteeing that it will
// draw an opaque rect the size of the layer. If it is not, then we must
// clear this canvas ourselves.
if (requires_clear_) {
canvas->clear(SK_ColorTRANSPARENT);
return;
}
if (clear_canvas_with_debug_color_)
canvas->clear(DebugColors::NonPaintedFillColor());
// If the canvas wants us to raster with complex transform, it is hard to
// determine the exact region we must clear. Just clear everything.
// TODO(trchen): Optimize the common case that transformed content bounds
// covers the whole clip region.
if (!canvas->getTotalMatrix().rectStaysRect()) {
canvas->clear(SK_ColorTRANSPARENT);
return;
}
SkRect content_device_rect;
canvas->getTotalMatrix().mapRect(
&content_device_rect, SkRect::MakeWH(size_.width(), size_.height()));
// The final texel of content may only be partially covered by a
// rasterization; this rect represents the content rect that is fully
// covered by content.
SkIRect opaque_rect;
content_device_rect.roundIn(&opaque_rect);
if (opaque_rect.contains(canvas->getDeviceClipBounds()))
return;
// Even if completely covered, for rasterizations that touch the edge of the
// layer, we also need to raster the background color underneath the last
// texel (since the recording won't cover it) and outside the last texel
// (due to linear filtering when using this texture).
SkIRect interest_rect;
content_device_rect.roundOut(&interest_rect);
interest_rect.outset(1, 1);
if (clear_canvas_with_debug_color_) {
// Any non-painted areas outside of the content bounds are left in
// this color. If this is seen then it means that cc neglected to
// rerasterize a tile that used to intersect with the content rect
// after the content bounds grew.
canvas->save();
// Use clipRegion to bypass CTM because the rects are device rects.
SkRegion interest_region;
interest_region.setRect(interest_rect);
canvas->clipRegion(interest_region, SkClipOp::kDifference);
canvas->clear(DebugColors::MissingResizeInvalidations());
canvas->restore();
}
// Drawing at most 2 x 2 x (canvas width + canvas height) texels is 2-3X
// faster than clearing, so special case this.
canvas->save();
// Use clipRegion to bypass CTM because the rects are device rects.
SkRegion interest_region;
interest_region.setRect(interest_rect);
interest_region.op(opaque_rect, SkRegion::kDifference_Op);
canvas->clipRegion(interest_region);
canvas->clear(background_color_);
canvas->restore();
}
void RasterSource::RasterCommon(SkCanvas* raster_canvas,
SkPicture::AbortCallback* callback) const {
DCHECK(display_list_.get());
int repeat_count = std::max(1, slow_down_raster_scale_factor_for_debug_);
for (int i = 0; i < repeat_count; ++i)
display_list_->Raster(raster_canvas, callback);
}
sk_sp<SkPicture> RasterSource::GetFlattenedPicture() {
TRACE_EVENT0("cc", "RasterSource::GetFlattenedPicture");
SkPictureRecorder recorder;
SkCanvas* canvas = recorder.beginRecording(size_.width(), size_.height());
if (!size_.IsEmpty()) {
PrepareForPlaybackToCanvas(canvas);
RasterCommon(canvas, nullptr);
}
return recorder.finishRecordingAsPicture();
}
size_t RasterSource::GetMemoryUsage() const {
if (!display_list_)
return 0;
return display_list_->ApproximateMemoryUsage() +
painter_reported_memory_usage_;
}
bool RasterSource::PerformSolidColorAnalysis(const gfx::Rect& content_rect,
float contents_scale,
SkColor* color) const {
TRACE_EVENT0("cc", "RasterSource::PerformSolidColorAnalysis");
gfx::Rect layer_rect =
gfx::ScaleToEnclosingRect(content_rect, 1.f / contents_scale);
layer_rect.Intersect(gfx::Rect(size_));
skia::AnalysisCanvas canvas(layer_rect.width(), layer_rect.height());
canvas.translate(-layer_rect.x(), -layer_rect.y());
// Note that because no color conversion is applied to solid color analysis,
// the resulting solid color will be known to be sRGB.
RasterCommon(&canvas, &canvas);
return canvas.GetColorIfSolid(color);
}
void RasterSource::GetDiscardableImagesInRect(
const gfx::Rect& layer_rect,
float contents_scale,
const gfx::ColorSpace& target_color_space,
std::vector<DrawImage>* images) const {
DCHECK_EQ(0u, images->size());
display_list_->GetDiscardableImagesInRect(layer_rect, contents_scale,
target_color_space, images);
}
gfx::Rect RasterSource::GetRectForImage(ImageId image_id) const {
if (!display_list_)
return gfx::Rect();
return display_list_->GetRectForImage(image_id);
}
bool RasterSource::CoversRect(const gfx::Rect& layer_rect) const {
if (size_.IsEmpty())
return false;
gfx::Rect bounded_rect = layer_rect;
bounded_rect.Intersect(gfx::Rect(size_));
return recorded_viewport_.Contains(bounded_rect);
}
gfx::Size RasterSource::GetSize() const {
return size_;
}
bool RasterSource::IsSolidColor() const {
return is_solid_color_;
}
SkColor RasterSource::GetSolidColor() const {
DCHECK(IsSolidColor());
return solid_color_;
}
bool RasterSource::HasRecordings() const {
return !!display_list_.get();
}
gfx::Rect RasterSource::RecordedViewport() const {
return recorded_viewport_;
}
void RasterSource::AsValueInto(base::trace_event::TracedValue* array) const {
if (display_list_.get())
TracedValue::AppendIDRef(display_list_.get(), array);
}
void RasterSource::DidBeginTracing() {
if (display_list_.get())
display_list_->EmitTraceSnapshot();
}
bool RasterSource::CanUseLCDText() const {
return can_use_lcd_text_;
}
scoped_refptr<RasterSource> RasterSource::CreateCloneWithoutLCDText() const {
bool can_use_lcd_text = false;
return scoped_refptr<RasterSource>(new RasterSource(this, can_use_lcd_text));
}
RasterSource::PlaybackSettings::PlaybackSettings()
: playback_to_shared_canvas(false),
skip_images(false),
use_image_hijack_canvas(true) {}
RasterSource::PlaybackSettings::PlaybackSettings(const PlaybackSettings&) =
default;
RasterSource::PlaybackSettings::PlaybackSettings(PlaybackSettings&&) = default;
RasterSource::PlaybackSettings::~PlaybackSettings() = default;
} // namespace cc