| // 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, |
| 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); |
| 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) { |
| 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, &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 |