1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/viz/service/display/software_renderer.h"
6 
7 #include <memory>
8 #include <utility>
9 #include <vector>
10 
11 #include "base/process/memory.h"
12 #include "base/trace_event/trace_event.h"
13 #include "cc/base/math_util.h"
14 #include "cc/paint/image_provider.h"
15 #include "cc/paint/render_surface_filters.h"
16 #include "components/viz/common/display/renderer_settings.h"
17 #include "components/viz/common/frame_sinks/copy_output_request.h"
18 #include "components/viz/common/frame_sinks/copy_output_util.h"
19 #include "components/viz/common/quads/aggregated_render_pass_draw_quad.h"
20 #include "components/viz/common/quads/compositor_render_pass_draw_quad.h"
21 #include "components/viz/common/quads/debug_border_draw_quad.h"
22 #include "components/viz/common/quads/picture_draw_quad.h"
23 #include "components/viz/common/quads/solid_color_draw_quad.h"
24 #include "components/viz/common/quads/texture_draw_quad.h"
25 #include "components/viz/common/quads/tile_draw_quad.h"
26 #include "components/viz/common/skia_helper.h"
27 #include "components/viz/common/viz_utils.h"
28 #include "components/viz/service/display/output_surface.h"
29 #include "components/viz/service/display/output_surface_frame.h"
30 #include "components/viz/service/display/renderer_utils.h"
31 #include "components/viz/service/display/software_output_device.h"
32 #include "skia/ext/image_operations.h"
33 #include "skia/ext/legacy_display_globals.h"
34 #include "skia/ext/opacity_filter_canvas.h"
35 #include "third_party/skia/include/core/SkCanvas.h"
36 #include "third_party/skia/include/core/SkColor.h"
37 #include "third_party/skia/include/core/SkImageFilter.h"
38 #include "third_party/skia/include/core/SkMatrix.h"
39 #include "third_party/skia/include/core/SkPath.h"
40 #include "third_party/skia/include/core/SkPoint.h"
41 #include "third_party/skia/include/core/SkShader.h"
42 #include "third_party/skia/include/effects/SkShaderMaskFilter.h"
43 #include "ui/gfx/geometry/axis_transform2d.h"
44 #include "ui/gfx/geometry/rect_conversions.h"
45 #include "ui/gfx/skia_util.h"
46 #include "ui/gfx/transform.h"
47 
48 namespace viz {
49 namespace {
50 class AnimatedImagesProvider : public cc::ImageProvider {
51  public:
AnimatedImagesProvider(const PictureDrawQuad::ImageAnimationMap * image_animation_map)52   AnimatedImagesProvider(
53       const PictureDrawQuad::ImageAnimationMap* image_animation_map)
54       : image_animation_map_(image_animation_map) {}
55   ~AnimatedImagesProvider() override = default;
56 
GetRasterContent(const cc::DrawImage & draw_image)57   ImageProvider::ScopedResult GetRasterContent(
58       const cc::DrawImage& draw_image) override {
59     // TODO(xidachen): Ensure this function works for paint worklet generated
60     // images.
61     const auto& paint_image = draw_image.paint_image();
62     auto it = image_animation_map_->find(paint_image.stable_id());
63     size_t frame_index = it == image_animation_map_->end()
64                              ? cc::PaintImage::kDefaultFrameIndex
65                              : it->second;
66     return ScopedResult(cc::DecodedDrawImage(
67         paint_image.GetSkImageForFrame(
68             frame_index, cc::PaintImage::kDefaultGeneratorClientId),
69         nullptr, SkSize::Make(0, 0), SkSize::Make(1.f, 1.f),
70         draw_image.filter_quality()));
71   }
72 
73  private:
74   const PictureDrawQuad::ImageAnimationMap* image_animation_map_;
75 };
76 
77 }  // namespace
78 
SoftwareRenderer(const RendererSettings * settings,const DebugRendererSettings * debug_settings,OutputSurface * output_surface,DisplayResourceProvider * resource_provider,OverlayProcessorInterface * overlay_processor)79 SoftwareRenderer::SoftwareRenderer(const RendererSettings* settings,
80                                    const DebugRendererSettings* debug_settings,
81                                    OutputSurface* output_surface,
82                                    DisplayResourceProvider* resource_provider,
83                                    OverlayProcessorInterface* overlay_processor)
84     : DirectRenderer(settings,
85                      debug_settings,
86                      output_surface,
87                      resource_provider,
88                      overlay_processor),
89       output_device_(output_surface->software_device()) {}
90 
~SoftwareRenderer()91 SoftwareRenderer::~SoftwareRenderer() {}
92 
CanPartialSwap()93 bool SoftwareRenderer::CanPartialSwap() {
94   return true;
95 }
96 
BeginDrawingFrame()97 void SoftwareRenderer::BeginDrawingFrame() {
98   TRACE_EVENT0("viz", "SoftwareRenderer::BeginDrawingFrame");
99 }
100 
FinishDrawingFrame()101 void SoftwareRenderer::FinishDrawingFrame() {
102   TRACE_EVENT0("viz", "SoftwareRenderer::FinishDrawingFrame");
103   current_framebuffer_canvas_.reset();
104   current_canvas_ = nullptr;
105 
106   if (root_canvas_)
107     output_device_->EndPaint();
108   root_canvas_ = nullptr;
109 }
110 
SwapBuffers(SwapFrameData swap_frame_data)111 void SoftwareRenderer::SwapBuffers(SwapFrameData swap_frame_data) {
112   DCHECK(visible_);
113   TRACE_EVENT0("viz", "SoftwareRenderer::SwapBuffers");
114   OutputSurfaceFrame output_frame;
115   output_frame.latency_info = std::move(swap_frame_data.latency_info);
116   output_frame.top_controls_visible_height_changed =
117       swap_frame_data.top_controls_visible_height_changed;
118   output_surface_->SwapBuffers(std::move(output_frame));
119 }
120 
FlippedFramebuffer() const121 bool SoftwareRenderer::FlippedFramebuffer() const {
122   return false;
123 }
124 
EnsureScissorTestEnabled()125 void SoftwareRenderer::EnsureScissorTestEnabled() {
126   is_scissor_enabled_ = true;
127 }
128 
EnsureScissorTestDisabled()129 void SoftwareRenderer::EnsureScissorTestDisabled() {
130   is_scissor_enabled_ = false;
131 }
132 
BindFramebufferToOutputSurface()133 void SoftwareRenderer::BindFramebufferToOutputSurface() {
134   DCHECK(!output_surface_->HasExternalStencilTest());
135   DCHECK(!root_canvas_);
136 
137   current_framebuffer_canvas_.reset();
138   root_canvas_ = output_device_->BeginPaint(current_frame()->root_damage_rect);
139   if (!root_canvas_)
140     output_device_->EndPaint();
141   current_canvas_ = root_canvas_;
142 }
143 
BindFramebufferToTexture(const AggregatedRenderPassId render_pass_id)144 void SoftwareRenderer::BindFramebufferToTexture(
145     const AggregatedRenderPassId render_pass_id) {
146   auto it = render_pass_bitmaps_.find(render_pass_id);
147   DCHECK(it != render_pass_bitmaps_.end());
148   SkBitmap& bitmap = it->second;
149 
150   current_framebuffer_canvas_ = std::make_unique<SkCanvas>(
151       bitmap, skia::LegacyDisplayGlobals::GetSkSurfaceProps());
152   current_canvas_ = current_framebuffer_canvas_.get();
153 }
154 
SetScissorTestRect(const gfx::Rect & scissor_rect)155 void SoftwareRenderer::SetScissorTestRect(const gfx::Rect& scissor_rect) {
156   is_scissor_enabled_ = true;
157   scissor_rect_ = scissor_rect;
158 }
159 
SetClipRect(const gfx::Rect & rect)160 void SoftwareRenderer::SetClipRect(const gfx::Rect& rect) {
161   if (!current_canvas_)
162     return;
163   // Skia applies the current matrix to clip rects so we reset it temporarily.
164   SkMatrix current_matrix = current_canvas_->getTotalMatrix();
165   current_canvas_->resetMatrix();
166 
167   // Checks below are incompatible with WebView as the canvas size and clip
168   // provided by Android or embedder app. And Chrome doesn't use
169   // SoftwareRenderer on Android.
170 #if !defined(OS_ANDROID)
171   // SetClipRect is assumed to be applied temporarily, on an
172   // otherwise-unclipped canvas.
173   DCHECK_EQ(current_canvas_->getDeviceClipBounds().width(),
174             current_canvas_->imageInfo().width());
175   DCHECK_EQ(current_canvas_->getDeviceClipBounds().height(),
176             current_canvas_->imageInfo().height());
177 #endif
178   current_canvas_->clipRect(gfx::RectToSkRect(rect));
179   current_canvas_->setMatrix(current_matrix);
180 }
181 
SetClipRRect(const gfx::RRectF & rrect)182 void SoftwareRenderer::SetClipRRect(const gfx::RRectF& rrect) {
183   if (!current_canvas_)
184     return;
185 
186   gfx::Transform screen_transform =
187       current_frame()->window_matrix * current_frame()->projection_matrix;
188   SkRRect result;
189   if (SkRRect(rrect).transform(SkMatrix(screen_transform.matrix()), &result)) {
190     // Skia applies the current matrix to clip rects so we reset it temporarily.
191     SkMatrix current_matrix = current_canvas_->getTotalMatrix();
192     current_canvas_->resetMatrix();
193     current_canvas_->clipRRect(result, true);
194     current_canvas_->setMatrix(current_matrix);
195   }
196 }
197 
ClearCanvas(SkColor color)198 void SoftwareRenderer::ClearCanvas(SkColor color) {
199   if (!current_canvas_)
200     return;
201 
202   if (is_scissor_enabled_) {
203     // The same paint used by SkCanvas::clear, but applied to the scissor rect.
204     SkPaint clear_paint;
205     clear_paint.setColor(color);
206     clear_paint.setBlendMode(SkBlendMode::kSrc);
207     current_canvas_->drawRect(gfx::RectToSkRect(scissor_rect_), clear_paint);
208   } else {
209     current_canvas_->clear(color);
210   }
211 }
212 
ClearFramebuffer()213 void SoftwareRenderer::ClearFramebuffer() {
214   if (current_frame()->current_render_pass->has_transparent_background) {
215     ClearCanvas(SkColorSetARGB(0, 0, 0, 0));
216   } else {
217 #ifndef NDEBUG
218     // On DEBUG builds, opaque render passes are cleared to blue
219     // to easily see regions that were not drawn on the screen.
220     ClearCanvas(SkColorSetARGB(255, 0, 0, 255));
221 #endif
222   }
223 }
224 
PrepareSurfaceForPass(SurfaceInitializationMode initialization_mode,const gfx::Rect & render_pass_scissor)225 void SoftwareRenderer::PrepareSurfaceForPass(
226     SurfaceInitializationMode initialization_mode,
227     const gfx::Rect& render_pass_scissor) {
228   switch (initialization_mode) {
229     case SURFACE_INITIALIZATION_MODE_PRESERVE:
230       EnsureScissorTestDisabled();
231       return;
232     case SURFACE_INITIALIZATION_MODE_FULL_SURFACE_CLEAR:
233       EnsureScissorTestDisabled();
234       ClearFramebuffer();
235       break;
236     case SURFACE_INITIALIZATION_MODE_SCISSORED_CLEAR:
237       SetScissorTestRect(render_pass_scissor);
238       ClearFramebuffer();
239       break;
240   }
241 }
242 
IsSoftwareResource(ResourceId resource_id) const243 bool SoftwareRenderer::IsSoftwareResource(ResourceId resource_id) const {
244   return resource_provider_->IsResourceSoftwareBacked(resource_id);
245 }
246 
DoDrawQuad(const DrawQuad * quad,const gfx::QuadF * draw_region)247 void SoftwareRenderer::DoDrawQuad(const DrawQuad* quad,
248                                   const gfx::QuadF* draw_region) {
249   if (!current_canvas_)
250     return;
251 
252   TRACE_EVENT0("viz", "SoftwareRenderer::DoDrawQuad");
253   const bool should_apply_rounded_corner = ShouldApplyRoundedCorner(quad);
254   bool do_save =
255       draw_region || is_scissor_enabled_ || should_apply_rounded_corner;
256   SkAutoCanvasRestore canvas_restore(current_canvas_, do_save);
257   if (is_scissor_enabled_) {
258     SetClipRect(scissor_rect_);
259   }
260 
261   if (should_apply_rounded_corner)
262     SetClipRRect(
263         quad->shared_quad_state->mask_filter_info.rounded_corner_bounds());
264 
265   gfx::Transform quad_rect_matrix;
266   QuadRectTransform(&quad_rect_matrix,
267                     quad->shared_quad_state->quad_to_target_transform,
268                     gfx::RectF(quad->rect));
269   gfx::Transform contents_device_transform =
270       current_frame()->window_matrix * current_frame()->projection_matrix *
271       quad_rect_matrix;
272   contents_device_transform.FlattenTo2d();
273   SkMatrix sk_device_matrix;
274   gfx::TransformToFlattenedSkMatrix(contents_device_transform,
275                                     &sk_device_matrix);
276   current_canvas_->setMatrix(sk_device_matrix);
277 
278   current_paint_.reset();
279   if (settings_->force_antialiasing ||
280       !IsScaleAndIntegerTranslate(sk_device_matrix)) {
281     // TODO(danakj): Until we can enable AA only on exterior edges of the
282     // layer, disable AA if any interior edges are present. crbug.com/248175
283     bool all_four_edges_are_exterior =
284         quad->IsTopEdge() && quad->IsLeftEdge() && quad->IsBottomEdge() &&
285         quad->IsRightEdge();
286     if (settings_->allow_antialiasing &&
287         (settings_->force_antialiasing || all_four_edges_are_exterior))
288       current_paint_.setAntiAlias(true);
289     current_paint_.setFilterQuality(kLow_SkFilterQuality);
290   }
291 
292   if (quad->ShouldDrawWithBlending() ||
293       quad->shared_quad_state->blend_mode != SkBlendMode::kSrcOver) {
294     current_paint_.setAlpha(quad->shared_quad_state->opacity * 255);
295     current_paint_.setBlendMode(quad->shared_quad_state->blend_mode);
296   } else {
297     current_paint_.setBlendMode(SkBlendMode::kSrc);
298   }
299 
300   if (draw_region) {
301     gfx::QuadF local_draw_region(*draw_region);
302     SkPath draw_region_clip_path;
303     local_draw_region -=
304         gfx::Vector2dF(quad->visible_rect.x(), quad->visible_rect.y());
305     local_draw_region.Scale(1.0f / quad->visible_rect.width(),
306                             1.0f / quad->visible_rect.height());
307     local_draw_region -= gfx::Vector2dF(0.5f, 0.5f);
308 
309     SkPoint clip_points[4];
310     QuadFToSkPoints(local_draw_region, clip_points);
311     draw_region_clip_path.addPoly(clip_points, 4, true);
312 
313     current_canvas_->clipPath(draw_region_clip_path);
314   }
315 
316   switch (quad->material) {
317     case DrawQuad::Material::kAggregatedRenderPass:
318       DrawRenderPassQuad(AggregatedRenderPassDrawQuad::MaterialCast(quad));
319       break;
320     case DrawQuad::Material::kDebugBorder:
321       DrawDebugBorderQuad(DebugBorderDrawQuad::MaterialCast(quad));
322       break;
323     case DrawQuad::Material::kPictureContent:
324       DrawPictureQuad(PictureDrawQuad::MaterialCast(quad));
325       break;
326     case DrawQuad::Material::kCompositorRenderPass:
327       // At this point, all RenderPassDrawQuads should be converted to
328       // AggregatedRenderPassDrawQuads.
329       NOTREACHED();
330       break;
331     case DrawQuad::Material::kSolidColor:
332       DrawSolidColorQuad(SolidColorDrawQuad::MaterialCast(quad));
333       break;
334     case DrawQuad::Material::kTextureContent:
335       DrawTextureQuad(TextureDrawQuad::MaterialCast(quad));
336       break;
337     case DrawQuad::Material::kTiledContent:
338       DrawTileQuad(TileDrawQuad::MaterialCast(quad));
339       break;
340     case DrawQuad::Material::kSurfaceContent:
341       // Surface content should be fully resolved to other quad types before
342       // reaching a direct renderer.
343       NOTREACHED();
344       break;
345     case DrawQuad::Material::kInvalid:
346     case DrawQuad::Material::kYuvVideoContent:
347     case DrawQuad::Material::kStreamVideoContent:
348       DrawUnsupportedQuad(quad);
349       NOTREACHED();
350       break;
351     case DrawQuad::Material::kVideoHole:
352       // VideoHoleDrawQuad should only be used by Cast, and should
353       // have been replaced by cast-specific OverlayProcessor before
354       // reach here. In non-cast build, an untrusted render could send such
355       // Quad and the quad would then reach here unexpectedly. Therefore
356       // we should skip NOTREACHED() so an untrusted render is not capable
357       // of causing a crash.
358       DrawUnsupportedQuad(quad);
359       break;
360   }
361 
362   current_canvas_->resetMatrix();
363 }
364 
DrawDebugBorderQuad(const DebugBorderDrawQuad * quad)365 void SoftwareRenderer::DrawDebugBorderQuad(const DebugBorderDrawQuad* quad) {
366   // We need to apply the matrix manually to have pixel-sized stroke width.
367   SkPoint vertices[4];
368   gfx::RectFToSkRect(QuadVertexRect()).toQuad(vertices);
369   SkPoint transformed_vertices[4];
370   current_canvas_->getTotalMatrix().mapPoints(transformed_vertices, vertices,
371                                               4);
372   current_canvas_->resetMatrix();
373 
374   current_paint_.setColor(quad->color);
375   current_paint_.setAlpha(quad->shared_quad_state->opacity *
376                           SkColorGetA(quad->color));
377   current_paint_.setStyle(SkPaint::kStroke_Style);
378   current_paint_.setStrokeWidth(quad->width);
379   current_canvas_->drawPoints(SkCanvas::kPolygon_PointMode, 4,
380                               transformed_vertices, current_paint_);
381 }
382 
DrawPictureQuad(const PictureDrawQuad * quad)383 void SoftwareRenderer::DrawPictureQuad(const PictureDrawQuad* quad) {
384   SkMatrix content_matrix;
385   content_matrix.setRectToRect(gfx::RectFToSkRect(quad->tex_coord_rect),
386                                gfx::RectFToSkRect(QuadVertexRect()),
387                                SkMatrix::kFill_ScaleToFit);
388   current_canvas_->concat(content_matrix);
389 
390   const bool needs_transparency =
391       SkScalarRoundToInt(quad->shared_quad_state->opacity * 255) < 255;
392   const bool disable_image_filtering =
393       disable_picture_quad_image_filtering_ || quad->nearest_neighbor;
394 
395   TRACE_EVENT0("viz", "SoftwareRenderer::DrawPictureQuad");
396 
397   SkCanvas* raster_canvas = current_canvas_;
398 
399   base::Optional<skia::OpacityFilterCanvas> opacity_canvas;
400   if (needs_transparency || disable_image_filtering) {
401     // TODO(aelias): This isn't correct in all cases. We should detect these
402     // cases and fall back to a persistent bitmap backing
403     // (http://crbug.com/280374).
404     // TODO(vmpstr): Fold this canvas into playback and have raster source
405     // accept a set of settings on playback that will determine which canvas to
406     // apply. (http://crbug.com/594679)
407     opacity_canvas.emplace(raster_canvas, quad->shared_quad_state->opacity,
408                            disable_image_filtering);
409     raster_canvas = &*opacity_canvas;
410   }
411 
412   // Treat all subnormal values as zero for performance.
413   cc::ScopedSubnormalFloatDisabler disabler;
414 
415   // Use an image provider to select the correct frame for animated images.
416   AnimatedImagesProvider image_provider(&quad->image_animation_map);
417 
418   raster_canvas->save();
419   raster_canvas->translate(-quad->content_rect.x(), -quad->content_rect.y());
420   raster_canvas->clipRect(gfx::RectToSkRect(quad->content_rect));
421   raster_canvas->scale(quad->contents_scale, quad->contents_scale);
422   quad->display_item_list->Raster(raster_canvas, &image_provider);
423   raster_canvas->restore();
424 }
425 
DrawSolidColorQuad(const SolidColorDrawQuad * quad)426 void SoftwareRenderer::DrawSolidColorQuad(const SolidColorDrawQuad* quad) {
427   gfx::RectF visible_quad_vertex_rect = cc::MathUtil::ScaleRectProportional(
428       QuadVertexRect(), gfx::RectF(quad->rect), gfx::RectF(quad->visible_rect));
429   current_paint_.setColor(quad->color);
430   current_paint_.setAlpha(quad->shared_quad_state->opacity *
431                           SkColorGetA(quad->color));
432   current_canvas_->drawRect(gfx::RectFToSkRect(visible_quad_vertex_rect),
433                             current_paint_);
434 }
435 
DrawTextureQuad(const TextureDrawQuad * quad)436 void SoftwareRenderer::DrawTextureQuad(const TextureDrawQuad* quad) {
437   if (!IsSoftwareResource(quad->resource_id())) {
438     DrawUnsupportedQuad(quad);
439     return;
440   }
441 
442   // TODO(skaslev): Add support for non-premultiplied alpha.
443   DisplayResourceProvider::ScopedReadLockSkImage lock(resource_provider_,
444                                                       quad->resource_id());
445   if (!lock.valid())
446     return;
447   const SkImage* image = lock.sk_image();
448   gfx::RectF uv_rect = gfx::ScaleRect(
449       gfx::BoundingRect(quad->uv_top_left, quad->uv_bottom_right),
450       image->width(), image->height());
451   gfx::RectF visible_uv_rect = cc::MathUtil::ScaleRectProportional(
452       uv_rect, gfx::RectF(quad->rect), gfx::RectF(quad->visible_rect));
453   SkRect sk_uv_rect = gfx::RectFToSkRect(visible_uv_rect);
454   gfx::RectF visible_quad_vertex_rect = cc::MathUtil::ScaleRectProportional(
455       QuadVertexRect(), gfx::RectF(quad->rect), gfx::RectF(quad->visible_rect));
456   SkRect quad_rect = gfx::RectFToSkRect(visible_quad_vertex_rect);
457 
458   if (quad->y_flipped)
459     current_canvas_->scale(1, -1);
460 
461   bool blend_background =
462       quad->background_color != SK_ColorTRANSPARENT && !image->isOpaque();
463   bool needs_layer = blend_background && (current_paint_.getAlpha() != 0xFF);
464   if (needs_layer) {
465     current_canvas_->saveLayerAlpha(&quad_rect, current_paint_.getAlpha());
466     current_paint_.setAlpha(0xFF);
467   }
468   if (blend_background) {
469     SkPaint background_paint;
470     background_paint.setColor(quad->background_color);
471     current_canvas_->drawRect(quad_rect, background_paint);
472   }
473   current_paint_.setFilterQuality(
474       quad->nearest_neighbor ? kNone_SkFilterQuality : kLow_SkFilterQuality);
475   current_canvas_->drawImageRect(image, sk_uv_rect, quad_rect, &current_paint_);
476   if (needs_layer)
477     current_canvas_->restore();
478 }
479 
DrawTileQuad(const TileDrawQuad * quad)480 void SoftwareRenderer::DrawTileQuad(const TileDrawQuad* quad) {
481   // |resource_provider_| can be NULL in resourceless software draws, which
482   // should never produce tile quads in the first place.
483   DCHECK(resource_provider_);
484   DCHECK(IsSoftwareResource(quad->resource_id()));
485 
486   DisplayResourceProvider::ScopedReadLockSkImage lock(resource_provider_,
487                                                       quad->resource_id());
488   if (!lock.valid())
489     return;
490 
491   gfx::RectF visible_tex_coord_rect = cc::MathUtil::ScaleRectProportional(
492       quad->tex_coord_rect, gfx::RectF(quad->rect),
493       gfx::RectF(quad->visible_rect));
494   gfx::RectF visible_quad_vertex_rect = cc::MathUtil::ScaleRectProportional(
495       QuadVertexRect(), gfx::RectF(quad->rect), gfx::RectF(quad->visible_rect));
496 
497   SkRect uv_rect = gfx::RectFToSkRect(visible_tex_coord_rect);
498   current_paint_.setFilterQuality(
499       quad->nearest_neighbor ? kNone_SkFilterQuality : kLow_SkFilterQuality);
500   current_canvas_->drawImageRect(lock.sk_image(), uv_rect,
501                                  gfx::RectFToSkRect(visible_quad_vertex_rect),
502                                  &current_paint_);
503 }
504 
DrawRenderPassQuad(const AggregatedRenderPassDrawQuad * quad)505 void SoftwareRenderer::DrawRenderPassQuad(
506     const AggregatedRenderPassDrawQuad* quad) {
507   auto it = render_pass_bitmaps_.find(quad->render_pass_id);
508   if (it == render_pass_bitmaps_.end())
509     return;
510   SkBitmap& source_bitmap = it->second;
511 
512   SkRect dest_rect = gfx::RectFToSkRect(QuadVertexRect());
513   SkRect dest_visible_rect =
514       gfx::RectFToSkRect(cc::MathUtil::ScaleRectProportional(
515           QuadVertexRect(), gfx::RectF(quad->rect),
516           gfx::RectF(quad->visible_rect)));
517   SkRect content_rect = RectFToSkRect(quad->tex_coord_rect);
518 
519   sk_sp<SkImage> filter_image;
520   const cc::FilterOperations* filters = FiltersForPass(quad->render_pass_id);
521   if (filters) {
522     DCHECK(!filters->IsEmpty());
523     auto paint_filter = cc::RenderSurfaceFilters::BuildImageFilter(
524         *filters, gfx::SizeF(source_bitmap.width(), source_bitmap.height()));
525     auto image_filter =
526         paint_filter ? paint_filter->cached_sk_filter_ : nullptr;
527     if (image_filter) {
528       SkIRect result_rect;
529       // TODO(ajuma): Apply the filter in the same pass as the content where
530       // possible (e.g. when there's no origin offset). See crbug.com/308201.
531       filter_image =
532           ApplyImageFilter(image_filter.get(), quad, source_bitmap,
533                            /* offset_expanded_bounds = */ true, &result_rect);
534       if (result_rect.isEmpty()) {
535         return;
536       }
537       if (filter_image) {
538         gfx::RectF rect = gfx::SkRectToRectF(SkRect::Make(result_rect));
539         dest_rect = dest_visible_rect =
540             gfx::RectFToSkRect(cc::MathUtil::ScaleRectProportional(
541                 QuadVertexRect(), gfx::RectF(quad->rect), rect));
542         content_rect =
543             SkRect::MakeWH(result_rect.width(), result_rect.height());
544       }
545     }
546   }
547 
548   SkMatrix content_mat;
549   content_mat.setRectToRect(content_rect, dest_rect,
550                             SkMatrix::kFill_ScaleToFit);
551 
552   sk_sp<SkShader> shader;
553   if (!filter_image) {
554     shader = source_bitmap.makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
555                                       &content_mat);
556   } else {
557     shader = filter_image->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
558                                       &content_mat);
559   }
560 
561   if (quad->mask_resource_id()) {
562     DisplayResourceProvider::ScopedReadLockSkImage mask_lock(
563         resource_provider_, quad->mask_resource_id());
564     if (!mask_lock.valid())
565       return;
566 
567     // Scale normalized uv rect into absolute texel coordinates.
568     SkRect mask_rect = gfx::RectFToSkRect(
569         gfx::ScaleRect(quad->mask_uv_rect, quad->mask_texture_size.width(),
570                        quad->mask_texture_size.height()));
571 
572     SkMatrix mask_mat;
573     mask_mat.setRectToRect(mask_rect, dest_rect, SkMatrix::kFill_ScaleToFit);
574 
575     current_paint_.setMaskFilter(
576         SkShaderMaskFilter::Make(mask_lock.sk_image()->makeShader(
577             SkTileMode::kClamp, SkTileMode::kClamp, &mask_mat)));
578   }
579 
580   // If we have a backdrop filter shader, render its results first.
581   sk_sp<SkShader> backdrop_filter_shader =
582       GetBackdropFilterShader(quad, SkTileMode::kClamp);
583   if (backdrop_filter_shader) {
584     SkPaint paint;
585     paint.setShader(std::move(backdrop_filter_shader));
586     paint.setMaskFilter(current_paint_.refMaskFilter());
587     current_canvas_->drawRect(dest_visible_rect, paint);
588   }
589   current_paint_.setShader(std::move(shader));
590   current_canvas_->drawRect(dest_visible_rect, current_paint_);
591 }
592 
DrawUnsupportedQuad(const DrawQuad * quad)593 void SoftwareRenderer::DrawUnsupportedQuad(const DrawQuad* quad) {
594 #ifdef NDEBUG
595   current_paint_.setColor(SK_ColorWHITE);
596 #else
597   current_paint_.setColor(SK_ColorMAGENTA);
598 #endif
599   current_paint_.setAlpha(quad->shared_quad_state->opacity * 255);
600   current_canvas_->drawRect(gfx::RectFToSkRect(QuadVertexRect()),
601                             current_paint_);
602 }
603 
CopyDrawnRenderPass(const copy_output::RenderPassGeometry & geometry,std::unique_ptr<CopyOutputRequest> request)604 void SoftwareRenderer::CopyDrawnRenderPass(
605     const copy_output::RenderPassGeometry& geometry,
606     std::unique_ptr<CopyOutputRequest> request) {
607   sk_sp<SkColorSpace> color_space =
608       CurrentRenderPassColorSpace().ToSkColorSpace();
609   DCHECK(color_space);
610 
611   SkBitmap bitmap;
612   if (request->is_scaled()) {
613     // Resolve the source for the scaling input: Initialize a SkPixmap that
614     // selects the current RenderPass's output rect within the current canvas
615     // and provides access to its pixels.
616     SkPixmap render_pass_output;
617     if (!current_canvas_->peekPixels(&render_pass_output))
618       return;
619     {
620       render_pass_output =
621           SkPixmap(render_pass_output.info()
622                        .makeWH(geometry.sampling_bounds.width(),
623                                geometry.sampling_bounds.height())
624                        .makeColorSpace(std::move(color_space)),
625                    render_pass_output.addr(geometry.sampling_bounds.x(),
626                                            geometry.sampling_bounds.y()),
627                    render_pass_output.rowBytes());
628     }
629 
630     // Execute the scaling: For downscaling, use the RESIZE_BETTER strategy
631     // (appropriate for thumbnailing); and, for upscaling, use the RESIZE_BEST
632     // strategy. Note that processing is only done on the subset of the
633     // RenderPass output that contributes to the result.
634     using skia::ImageOperations;
635     const bool is_downscale_in_both_dimensions =
636         request->scale_to().x() < request->scale_from().x() &&
637         request->scale_to().y() < request->scale_from().y();
638     const ImageOperations::ResizeMethod method =
639         is_downscale_in_both_dimensions ? ImageOperations::RESIZE_BETTER
640                                         : ImageOperations::RESIZE_BEST;
641     bitmap = ImageOperations::Resize(
642         render_pass_output, method, geometry.result_bounds.width(),
643         geometry.result_bounds.height(),
644         SkIRect{geometry.result_selection.x(), geometry.result_selection.y(),
645                 geometry.result_selection.right(),
646                 geometry.result_selection.bottom()});
647   } else /* if (!request->is_scaled()) */ {
648     SkImageInfo info = SkImageInfo::MakeN32Premul(
649         geometry.result_selection.width(), geometry.result_selection.height(),
650         std::move(color_space));
651     if (!bitmap.tryAllocPixels(info))
652       return;
653 
654     if (!current_canvas_->readPixels(bitmap, geometry.readback_offset.x(),
655                                      geometry.readback_offset.y()))
656       return;
657   }
658 
659   // Deliver the result. SoftwareRenderer supports RGBA_BITMAP and I420_PLANES
660   // only. For legacy reasons, if a RGBA_TEXTURE request is being made, clients
661   // are prepared to accept RGBA_BITMAP results.
662   //
663   // TODO(crbug/754872): Get rid of the legacy behavior and send empty results
664   // for RGBA_TEXTURE requests once tab capture is moved into VIZ.
665   const CopyOutputResult::Format result_format =
666       (request->result_format() == CopyOutputResult::Format::RGBA_TEXTURE)
667           ? CopyOutputResult::Format::RGBA_BITMAP
668           : request->result_format();
669   // Note: The CopyOutputSkBitmapResult automatically provides I420 format
670   // conversion, if needed.
671   request->SendResult(std::make_unique<CopyOutputSkBitmapResult>(
672       result_format, geometry.result_selection, bitmap));
673 }
674 
DidChangeVisibility()675 void SoftwareRenderer::DidChangeVisibility() {
676   if (visible_)
677     output_surface_->EnsureBackbuffer();
678   else
679     output_surface_->DiscardBackbuffer();
680 }
681 
GenerateMipmap()682 void SoftwareRenderer::GenerateMipmap() {
683   NOTIMPLEMENTED();
684 }
685 
ShouldApplyBackdropFilters(const cc::FilterOperations * backdrop_filters,const AggregatedRenderPassDrawQuad * quad) const686 bool SoftwareRenderer::ShouldApplyBackdropFilters(
687     const cc::FilterOperations* backdrop_filters,
688     const AggregatedRenderPassDrawQuad* quad) const {
689   if (!backdrop_filters)
690     return false;
691   if (quad->shared_quad_state->opacity == 0.f)
692     return false;
693   DCHECK(!backdrop_filters->IsEmpty());
694   return true;
695 }
696 
697 // Applies |filter| to |to_filter| bitmap. |result_rect| will be filled with the
698 // automatically-computed destination bounds. If |offset_expanded_bounds| is
699 // true, the bitmap will be offset for any pixel-moving filters. This function
700 // is called for both filters and backdrop_filters. The difference between those
701 // two paths is that the filter path wants to offset to the expanded bounds
702 // (including border for pixel moving filters) when drawing the bitmap into the
703 // canvas, while the backdrop filter path needs to keep the origin unmoved (at
704 // quad->rect origin) so that it gets put in the right spot relative to the
705 // underlying backdrop.
ApplyImageFilter(SkImageFilter * filter,const AggregatedRenderPassDrawQuad * quad,const SkBitmap & to_filter,bool offset_expanded_bounds,SkIRect * result_rect) const706 sk_sp<SkImage> SoftwareRenderer::ApplyImageFilter(
707     SkImageFilter* filter,
708     const AggregatedRenderPassDrawQuad* quad,
709     const SkBitmap& to_filter,
710     bool offset_expanded_bounds,
711     SkIRect* result_rect) const {
712   DCHECK(result_rect);
713   if (!filter)
714     return nullptr;
715 
716   SkMatrix local_matrix;
717   local_matrix.setTranslate(quad->filters_origin.x(), quad->filters_origin.y());
718   local_matrix.postScale(quad->filters_scale.x(), quad->filters_scale.y());
719   *result_rect =
720       filter->filterBounds(gfx::RectToSkIRect(quad->rect), local_matrix,
721                            SkImageFilter::kForward_MapDirection);
722   gfx::Point canvas_offset =
723       offset_expanded_bounds ? gfx::Point(result_rect->x(), result_rect->y())
724                              : quad->rect.origin();
725   SkImageInfo dst_info =
726       SkImageInfo::MakeN32Premul(result_rect->width(), result_rect->height());
727   sk_sp<SkSurface> surface = SkSurface::MakeRaster(dst_info);
728   if (!surface)
729     return nullptr;
730 
731   SkPaint paint;
732   // Treat subnormal float values as zero for performance.
733   cc::ScopedSubnormalFloatDisabler disabler;
734   paint.setImageFilter(filter->makeWithLocalMatrix(local_matrix));
735   surface->getCanvas()->translate(-canvas_offset.x(), -canvas_offset.y());
736   surface->getCanvas()->drawBitmap(to_filter, quad->rect.x(), quad->rect.y(),
737                                    &paint);
738   return surface->makeImageSnapshot();
739 }
740 
GetBackdropBitmap(const gfx::Rect & bounding_rect) const741 SkBitmap SoftwareRenderer::GetBackdropBitmap(
742     const gfx::Rect& bounding_rect) const {
743   SkImageInfo info =
744       SkImageInfo::MakeN32Premul(bounding_rect.width(), bounding_rect.height());
745   SkBitmap bitmap;
746   if (!bitmap.tryAllocPixels(info))
747     base::TerminateBecauseOutOfMemory(info.computeMinByteSize());
748 
749   if (!current_canvas_->readPixels(bitmap, bounding_rect.x(),
750                                    bounding_rect.y()))
751     bitmap.reset();
752   return bitmap;
753 }
754 
GetBackdropBoundingBoxForRenderPassQuad(const AggregatedRenderPassDrawQuad * quad,const cc::FilterOperations * backdrop_filters,base::Optional<gfx::RRectF> backdrop_filter_bounds_input,gfx::Transform contents_device_transform,gfx::Transform * backdrop_filter_bounds_transform,base::Optional<gfx::RRectF> * backdrop_filter_bounds,gfx::Rect * unclipped_rect) const755 gfx::Rect SoftwareRenderer::GetBackdropBoundingBoxForRenderPassQuad(
756     const AggregatedRenderPassDrawQuad* quad,
757     const cc::FilterOperations* backdrop_filters,
758     base::Optional<gfx::RRectF> backdrop_filter_bounds_input,
759     gfx::Transform contents_device_transform,
760     gfx::Transform* backdrop_filter_bounds_transform,
761     base::Optional<gfx::RRectF>* backdrop_filter_bounds,
762     gfx::Rect* unclipped_rect) const {
763   DCHECK(backdrop_filter_bounds_transform);
764   DCHECK(backdrop_filter_bounds);
765   DCHECK(unclipped_rect);
766 
767   // |backdrop_filter_bounds| is a rounded rect in [-0.5,0.5] space that
768   // represents |backdrop_filter_bounds_input| as a fraction of the space
769   // defined by |quad->rect|, not including its offset.
770   *backdrop_filter_bounds = gfx::RRectF();
771   if (!backdrop_filter_bounds_input ||
772       !GetScaledRRectF(quad->rect, backdrop_filter_bounds_input.value(),
773                        &backdrop_filter_bounds->value())) {
774     backdrop_filter_bounds->reset();
775   }
776 
777   // |backdrop_rect| is now the bounding box of clip_region, in window pixel
778   // coordinates, and with flip applied.
779   gfx::Rect backdrop_rect = gfx::ToEnclosingRect(cc::MathUtil::MapClippedRect(
780       contents_device_transform, QuadVertexRect()));
781 
782   *unclipped_rect = backdrop_rect;
783   backdrop_rect.Intersect(MoveFromDrawToWindowSpace(
784       current_frame()->current_render_pass->output_rect));
785 
786   // Shift to the space of the captured backdrop image.
787   *backdrop_filter_bounds_transform = contents_device_transform;
788   backdrop_filter_bounds_transform->PostTranslate(-backdrop_rect.x(),
789                                                   -backdrop_rect.y());
790 
791   return backdrop_rect;
792 }
793 
GetBackdropFilterShader(const AggregatedRenderPassDrawQuad * quad,SkTileMode content_tile_mode) const794 sk_sp<SkShader> SoftwareRenderer::GetBackdropFilterShader(
795     const AggregatedRenderPassDrawQuad* quad,
796     SkTileMode content_tile_mode) const {
797   const cc::FilterOperations* backdrop_filters =
798       BackdropFiltersForPass(quad->render_pass_id);
799   if (!ShouldApplyBackdropFilters(backdrop_filters, quad))
800     return nullptr;
801   base::Optional<gfx::RRectF> backdrop_filter_bounds_input =
802       BackdropFilterBoundsForPass(quad->render_pass_id);
803   DCHECK(!FiltersForPass(quad->render_pass_id))
804       << "Filters should always be in a separate Effect node";
805   if (backdrop_filter_bounds_input.has_value()) {
806     backdrop_filter_bounds_input->Scale(quad->filters_scale.x(),
807                                         quad->filters_scale.y());
808   }
809 
810   gfx::Transform quad_rect_matrix;
811   QuadRectTransform(&quad_rect_matrix,
812                     quad->shared_quad_state->quad_to_target_transform,
813                     gfx::RectF(quad->rect));
814   gfx::Transform contents_device_transform =
815       current_frame()->window_matrix * current_frame()->projection_matrix *
816       quad_rect_matrix;
817   contents_device_transform.FlattenTo2d();
818 
819   base::Optional<gfx::RRectF> backdrop_filter_bounds;
820   gfx::Transform backdrop_filter_bounds_transform;
821   gfx::Rect unclipped_rect;
822   gfx::Rect backdrop_rect = GetBackdropBoundingBoxForRenderPassQuad(
823       quad, backdrop_filters, backdrop_filter_bounds_input,
824       contents_device_transform, &backdrop_filter_bounds_transform,
825       &backdrop_filter_bounds, &unclipped_rect);
826 
827   // Figure out the transformations to move it back to pixel space.
828   gfx::Transform contents_device_transform_inverse;
829   if (!contents_device_transform.GetInverse(&contents_device_transform_inverse))
830     return nullptr;
831 
832   SkMatrix filter_backdrop_transform =
833       SkMatrix(contents_device_transform_inverse.matrix());
834   filter_backdrop_transform.preTranslate(backdrop_rect.x(), backdrop_rect.y());
835 
836   SkBitmap backdrop_bitmap = GetBackdropBitmap(backdrop_rect);
837   gfx::Point image_offset = gfx::Point(0, 0);
838   if (backdrop_filter_bounds.has_value()) {
839     gfx::Rect filter_clip = gfx::ToEnclosingRect(cc::MathUtil::MapClippedRect(
840         backdrop_filter_bounds_transform, backdrop_filter_bounds->rect()));
841     filter_clip.Intersect(
842         gfx::Rect(backdrop_bitmap.width(), backdrop_bitmap.height()));
843     if (filter_clip.IsEmpty())
844       return nullptr;
845     // Crop the source image to the backdrop_filter_bounds.
846     sk_sp<SkImage> cropped_image = SkImage::MakeFromBitmap(backdrop_bitmap);
847     cropped_image =
848         cropped_image->makeSubset(RectToSkIRect(filter_clip), nullptr);
849     cropped_image->asLegacyBitmap(&backdrop_bitmap);
850     image_offset = filter_clip.origin();
851   }
852 
853   gfx::Vector2dF clipping_offset =
854       (unclipped_rect.top_right() - backdrop_rect.top_right()) +
855       (backdrop_rect.bottom_left() - unclipped_rect.bottom_left());
856 
857   sk_sp<cc::PaintFilter> paint_filter =
858       cc::RenderSurfaceFilters::BuildImageFilter(
859           *backdrop_filters,
860           gfx::SizeF(backdrop_bitmap.width(), backdrop_bitmap.height()),
861           clipping_offset);
862   if (!paint_filter)
863     return nullptr;
864   sk_sp<SkImageFilter> filter = paint_filter->cached_sk_filter_;
865 
866   // TODO(989238): Software renderer does not support/implement kClamp_TileMode.
867   SkIRect result_rect;
868   sk_sp<SkImage> filtered_image =
869       ApplyImageFilter(filter.get(), quad, backdrop_bitmap,
870                        /* offset_expanded_bounds = */ false, &result_rect);
871   if (!filtered_image)
872     return nullptr;
873 
874   // Use an SkBitmap to paint the rrect-clipped filtered image.
875   SkImageInfo info =
876       SkImageInfo::MakeN32Premul(backdrop_rect.width(), backdrop_rect.height());
877   SkBitmap bitmap;
878   if (!bitmap.tryAllocPixels(info))
879     base::TerminateBecauseOutOfMemory(info.computeMinByteSize());
880 
881   SkCanvas canvas(bitmap, skia::LegacyDisplayGlobals::GetSkSurfaceProps());
882 
883   // Clip the filtered image to the (rounded) bounding box of the element.
884   if (backdrop_filter_bounds) {
885     canvas.setMatrix(SkMatrix(backdrop_filter_bounds_transform.matrix()));
886     canvas.clipRRect(SkRRect(*backdrop_filter_bounds), SkClipOp::kIntersect,
887                      true /* antialias */);
888     canvas.resetMatrix();
889   }
890 
891   // Paint the filtered backdrop image with opacity.
892   SkPaint paint;
893   if (quad->shared_quad_state->opacity < 1.0) {
894     paint.setImageFilter(
895         SkiaHelper::BuildOpacityFilter(quad->shared_quad_state->opacity));
896   }
897 
898   // Now paint the pre-filtered image onto the canvas.
899   SkRect src_rect =
900       SkRect::MakeXYWH(0, 0, backdrop_bitmap.width(), backdrop_bitmap.height());
901   SkRect dst_rect = src_rect.makeOffset(image_offset.x(), image_offset.y());
902   canvas.drawImageRect(filtered_image, src_rect, dst_rect, &paint);
903 
904   return SkImage::MakeFromBitmap(bitmap)->makeShader(
905       content_tile_mode, content_tile_mode, &filter_backdrop_transform);
906 }
907 
UpdateRenderPassTextures(const AggregatedRenderPassList & render_passes_in_draw_order,const base::flat_map<AggregatedRenderPassId,RenderPassRequirements> & render_passes_in_frame)908 void SoftwareRenderer::UpdateRenderPassTextures(
909     const AggregatedRenderPassList& render_passes_in_draw_order,
910     const base::flat_map<AggregatedRenderPassId, RenderPassRequirements>&
911         render_passes_in_frame) {
912   std::vector<AggregatedRenderPassId> passes_to_delete;
913   for (const auto& pair : render_pass_bitmaps_) {
914     auto render_pass_it = render_passes_in_frame.find(pair.first);
915     if (render_pass_it == render_passes_in_frame.end()) {
916       passes_to_delete.push_back(pair.first);
917       continue;
918     }
919 
920     gfx::Size required_size = render_pass_it->second.size;
921     // The RenderPassRequirements have a hint, which is only used for gpu
922     // compositing so it is ignored here.
923     const SkBitmap& bitmap = pair.second;
924 
925     bool size_appropriate = bitmap.width() >= required_size.width() &&
926                             bitmap.height() >= required_size.height();
927     if (!size_appropriate)
928       passes_to_delete.push_back(pair.first);
929   }
930 
931   // Delete RenderPass bitmaps from the previous frame that will not be used
932   // again.
933   for (const AggregatedRenderPassId& id : passes_to_delete)
934     render_pass_bitmaps_.erase(id);
935 }
936 
AllocateRenderPassResourceIfNeeded(const AggregatedRenderPassId & render_pass_id,const RenderPassRequirements & requirements)937 void SoftwareRenderer::AllocateRenderPassResourceIfNeeded(
938     const AggregatedRenderPassId& render_pass_id,
939     const RenderPassRequirements& requirements) {
940   auto it = render_pass_bitmaps_.find(render_pass_id);
941   if (it != render_pass_bitmaps_.end()) {
942     DCHECK(it->second.width() >= requirements.size.width() &&
943            it->second.height() >= requirements.size.height());
944     return;
945   }
946 
947   // The |requirements.mipmap| is only used for gpu-based rendering, so not used
948   // here.
949   //
950   // ColorSpace correctness for software compositing is a performance nightmare,
951   // so we don't do it. If we did, then the color space of the current frame's
952   // |current_render_pass| should be stored somewhere, but we should not set it
953   // on the bitmap itself. Instead, we'd use it with a SkColorSpaceXformCanvas
954   // that wraps the SkCanvas drawing into the bitmap.
955   SkImageInfo info =
956       SkImageInfo::MakeN32(requirements.size.width(),
957                            requirements.size.height(), kPremul_SkAlphaType);
958   SkBitmap bitmap;
959   if (!bitmap.tryAllocPixels(info))
960     base::TerminateBecauseOutOfMemory(info.computeMinByteSize());
961 
962   render_pass_bitmaps_.emplace(render_pass_id, std::move(bitmap));
963 }
964 
IsRenderPassResourceAllocated(const AggregatedRenderPassId & render_pass_id) const965 bool SoftwareRenderer::IsRenderPassResourceAllocated(
966     const AggregatedRenderPassId& render_pass_id) const {
967   auto it = render_pass_bitmaps_.find(render_pass_id);
968   return it != render_pass_bitmaps_.end();
969 }
970 
GetRenderPassBackingPixelSize(const AggregatedRenderPassId & render_pass_id)971 gfx::Size SoftwareRenderer::GetRenderPassBackingPixelSize(
972     const AggregatedRenderPassId& render_pass_id) {
973   auto it = render_pass_bitmaps_.find(render_pass_id);
974   DCHECK(it != render_pass_bitmaps_.end());
975   SkBitmap& bitmap = it->second;
976   return gfx::Size(bitmap.width(), bitmap.height());
977 }
978 
979 }  // namespace viz
980