blob: d51b6ab97f6b7bdfa2ed497259895dd17e66739e [file] [log] [blame]
// Copyright 2017 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/paint/scoped_raster_flags.h"
#include "cc/paint/image_provider.h"
#include "cc/paint/image_transfer_cache_entry.h"
#include "cc/paint/paint_filter.h"
#include "cc/paint/paint_image_builder.h"
namespace cc {
ScopedRasterFlags::ScopedRasterFlags(const PaintFlags* flags,
ImageProvider* image_provider,
const SkMatrix& ctm,
int max_texture_size,
uint8_t alpha)
: original_flags_(flags) {
if (image_provider) {
decode_stashing_image_provider_.emplace(image_provider);
// We skip the op if any images fail to decode.
DecodeImageShader(ctm);
if (decode_failed_)
return;
DecodeRecordShader(ctm, max_texture_size);
if (decode_failed_)
return;
DecodeFilter();
if (decode_failed_)
return;
}
if (alpha != 255) {
DCHECK(flags->SupportsFoldingAlpha());
MutableFlags()->setAlpha(SkMulDiv255Round(flags->getAlpha(), alpha));
}
AdjustStrokeIfNeeded(ctm);
}
ScopedRasterFlags::~ScopedRasterFlags() = default;
void ScopedRasterFlags::DecodeImageShader(const SkMatrix& ctm) {
if (!flags()->HasShader() ||
flags()->getShader()->shader_type() != PaintShader::Type::kImage)
return;
uint32_t transfer_cache_entry_id = kInvalidImageTransferCacheEntryId;
SkFilterQuality raster_quality = flags()->getFilterQuality();
bool transfer_cache_entry_needs_mips = false;
auto decoded_shader = flags()->getShader()->CreateDecodedImage(
ctm, flags()->getFilterQuality(), &*decode_stashing_image_provider_,
&transfer_cache_entry_id, &raster_quality,
&transfer_cache_entry_needs_mips);
DCHECK_EQ(transfer_cache_entry_id, kInvalidImageTransferCacheEntryId);
DCHECK_EQ(transfer_cache_entry_needs_mips, false);
if (!decoded_shader) {
decode_failed_ = true;
return;
}
MutableFlags()->setFilterQuality(raster_quality);
MutableFlags()->setShader(decoded_shader);
}
void ScopedRasterFlags::DecodeRecordShader(const SkMatrix& ctm,
int max_texture_size) {
if (!flags()->HasShader() ||
flags()->getShader()->shader_type() != PaintShader::Type::kPaintRecord)
return;
// Only replace shaders with animated images. Creating transient shaders for
// replacing decodes during raster results in cache misses in skia's picture
// shader cache, which results in re-rasterizing the picture for every draw.
if (flags()->getShader()->image_analysis_state() !=
ImageAnalysisState::kAnimatedImages) {
return;
}
gfx::SizeF raster_scale(1.f, 1.f);
auto decoded_shader = flags()->getShader()->CreateScaledPaintRecord(
ctm, max_texture_size, &raster_scale);
decoded_shader->CreateSkShader(&raster_scale,
&*decode_stashing_image_provider_);
MutableFlags()->setShader(std::move(decoded_shader));
}
void ScopedRasterFlags::DecodeFilter() {
if (!flags()->getImageFilter() ||
!flags()->getImageFilter()->has_discardable_images() ||
flags()->getImageFilter()->image_analysis_state() !=
ImageAnalysisState::kAnimatedImages) {
return;
}
MutableFlags()->setImageFilter(flags()->getImageFilter()->SnapshotWithImages(
&*decode_stashing_image_provider_));
}
void ScopedRasterFlags::AdjustStrokeIfNeeded(const SkMatrix& ctm) {
// With anti-aliasing turned off, strokes with a device space width in (0, 1)
// may not raster at all. To avoid this, we have two options:
//
// 1) force a hairline stroke (stroke-width == 0)
// 2) force anti-aliasing on
SkSize scale;
if (flags()->isAntiAlias() || // safe to raster
flags()->getStyle() == PaintFlags::kFill_Style || // not a stroke
!flags()->getStrokeWidth() || // explicit hairline
!ctm.decomposeScale(&scale)) { // cannot decompose
return;
}
const auto stroke_vec =
SkVector::Make(flags()->getStrokeWidth() * scale.width(),
flags()->getStrokeWidth() * scale.height());
if (stroke_vec.x() >= 1.f && stroke_vec.y() >= 1.f)
return; // safe to raster
const auto can_substitute_hairline =
flags()->getStrokeCap() == PaintFlags::kDefault_Cap &&
flags()->getStrokeJoin() == PaintFlags::kDefault_Join;
if (can_substitute_hairline && stroke_vec.x() < 1.f && stroke_vec.y() < 1.f) {
// Use modulated hairline when possible, as it is faster and produces
// results closer to the original intent.
MutableFlags()->setStrokeWidth(0);
MutableFlags()->setAlpha(std::round(
flags()->getAlpha() * std::sqrt(stroke_vec.x() * stroke_vec.y())));
return;
}
// Fall back to anti-aliasing.
MutableFlags()->setAntiAlias(true);
}
} // namespace cc