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 "cc/output/software_renderer.h"
6
7#include "base/debug/trace_event.h"
8#include "cc/base/math_util.h"
9#include "cc/output/compositor_frame.h"
10#include "cc/output/compositor_frame_ack.h"
11#include "cc/output/compositor_frame_metadata.h"
12#include "cc/output/copy_output_request.h"
13#include "cc/output/output_surface.h"
14#include "cc/output/software_output_device.h"
15#include "cc/quads/checkerboard_draw_quad.h"
16#include "cc/quads/debug_border_draw_quad.h"
17#include "cc/quads/picture_draw_quad.h"
18#include "cc/quads/render_pass_draw_quad.h"
19#include "cc/quads/solid_color_draw_quad.h"
20#include "cc/quads/texture_draw_quad.h"
21#include "cc/quads/tile_draw_quad.h"
22#include "skia/ext/opacity_draw_filter.h"
23#include "third_party/skia/include/core/SkCanvas.h"
24#include "third_party/skia/include/core/SkColor.h"
25#include "third_party/skia/include/core/SkDevice.h"
26#include "third_party/skia/include/core/SkMatrix.h"
27#include "third_party/skia/include/core/SkShader.h"
28#include "third_party/skia/include/effects/SkLayerRasterizer.h"
29#include "ui/gfx/rect_conversions.h"
30#include "ui/gfx/skia_util.h"
31#include "ui/gfx/transform.h"
32
33namespace cc {
34
35namespace {
36
37static inline bool IsScalarNearlyInteger(SkScalar scalar) {
38  return SkScalarNearlyZero(scalar - SkScalarRound(scalar));
39}
40
41bool IsScaleAndIntegerTranslate(const SkMatrix& matrix) {
42  return IsScalarNearlyInteger(matrix[SkMatrix::kMTransX]) &&
43         IsScalarNearlyInteger(matrix[SkMatrix::kMTransY]) &&
44         SkScalarNearlyZero(matrix[SkMatrix::kMSkewX]) &&
45         SkScalarNearlyZero(matrix[SkMatrix::kMSkewY]) &&
46         SkScalarNearlyZero(matrix[SkMatrix::kMPersp0]) &&
47         SkScalarNearlyZero(matrix[SkMatrix::kMPersp1]) &&
48         SkScalarNearlyZero(matrix[SkMatrix::kMPersp2] - 1.0f);
49}
50
51}  // anonymous namespace
52
53scoped_ptr<SoftwareRenderer> SoftwareRenderer::Create(
54    RendererClient* client,
55    OutputSurface* output_surface,
56    ResourceProvider* resource_provider) {
57  return make_scoped_ptr(
58      new SoftwareRenderer(client, output_surface, resource_provider));
59}
60
61SoftwareRenderer::SoftwareRenderer(RendererClient* client,
62                                   OutputSurface* output_surface,
63                                   ResourceProvider* resource_provider)
64  : DirectRenderer(client, output_surface, resource_provider),
65    visible_(true),
66    is_scissor_enabled_(false),
67    output_device_(output_surface->software_device()),
68    current_canvas_(NULL) {
69  if (resource_provider_) {
70    capabilities_.max_texture_size = resource_provider_->max_texture_size();
71    capabilities_.best_texture_format =
72        resource_provider_->best_texture_format();
73  }
74  capabilities_.using_set_visibility = true;
75  // The updater can access bitmaps while the SoftwareRenderer is using them.
76  capabilities_.allow_partial_texture_updates = true;
77  capabilities_.using_partial_swap = true;
78
79  capabilities_.using_map_image = Settings().use_map_image;
80  capabilities_.using_shared_memory_resources = true;
81}
82
83SoftwareRenderer::~SoftwareRenderer() {}
84
85const RendererCapabilities& SoftwareRenderer::Capabilities() const {
86  return capabilities_;
87}
88
89void SoftwareRenderer::BeginDrawingFrame(DrawingFrame* frame) {
90  TRACE_EVENT0("cc", "SoftwareRenderer::BeginDrawingFrame");
91  root_canvas_ = output_device_->BeginPaint(
92      gfx::ToEnclosingRect(frame->root_damage_rect));
93}
94
95void SoftwareRenderer::FinishDrawingFrame(DrawingFrame* frame) {
96  TRACE_EVENT0("cc", "SoftwareRenderer::FinishDrawingFrame");
97  current_framebuffer_lock_.reset();
98  current_canvas_ = NULL;
99  root_canvas_ = NULL;
100
101  current_frame_data_.reset(new SoftwareFrameData);
102  output_device_->EndPaint(current_frame_data_.get());
103}
104
105void SoftwareRenderer::SwapBuffers() {
106  CompositorFrame compositor_frame;
107  compositor_frame.metadata = client_->MakeCompositorFrameMetadata();
108  compositor_frame.software_frame_data = current_frame_data_.Pass();
109  output_surface_->SwapBuffers(&compositor_frame);
110}
111
112void SoftwareRenderer::ReceiveSwapBuffersAck(const CompositorFrameAck& ack) {
113  output_device_->ReclaimSoftwareFrame(ack.last_software_frame_id);
114}
115
116bool SoftwareRenderer::FlippedFramebuffer() const {
117  return false;
118}
119
120void SoftwareRenderer::EnsureScissorTestEnabled() {
121  is_scissor_enabled_ = true;
122  SetClipRect(scissor_rect_);
123}
124
125void SoftwareRenderer::EnsureScissorTestDisabled() {
126  // There is no explicit notion of enabling/disabling scissoring in software
127  // rendering, but the underlying effect we want is to clear any existing
128  // clipRect on the current SkCanvas. This is done by setting clipRect to
129  // the viewport's dimensions.
130  is_scissor_enabled_ = false;
131  SkDevice* device = current_canvas_->getDevice();
132  SetClipRect(gfx::Rect(device->width(), device->height()));
133}
134
135void SoftwareRenderer::Finish() {}
136
137void SoftwareRenderer::BindFramebufferToOutputSurface(DrawingFrame* frame) {
138  DCHECK(!client_->ExternalStencilTestEnabled());
139  current_framebuffer_lock_.reset();
140  current_canvas_ = root_canvas_;
141}
142
143bool SoftwareRenderer::BindFramebufferToTexture(
144    DrawingFrame* frame,
145    const ScopedResource* texture,
146    gfx::Rect target_rect) {
147  current_framebuffer_lock_.reset();
148  current_framebuffer_lock_ = make_scoped_ptr(
149      new ResourceProvider::ScopedWriteLockSoftware(
150          resource_provider_, texture->id()));
151  current_canvas_ = current_framebuffer_lock_->sk_canvas();
152  InitializeViewport(frame,
153                     target_rect,
154                     gfx::Rect(target_rect.size()),
155                     target_rect.size());
156  return true;
157}
158
159void SoftwareRenderer::SetScissorTestRect(gfx::Rect scissor_rect) {
160  is_scissor_enabled_ = true;
161  scissor_rect_ = scissor_rect;
162  SetClipRect(scissor_rect);
163}
164
165void SoftwareRenderer::SetClipRect(gfx::Rect rect) {
166  // Skia applies the current matrix to clip rects so we reset it temporary.
167  SkMatrix current_matrix = current_canvas_->getTotalMatrix();
168  current_canvas_->resetMatrix();
169  current_canvas_->clipRect(gfx::RectToSkRect(rect), SkRegion::kReplace_Op);
170  current_canvas_->setMatrix(current_matrix);
171}
172
173void SoftwareRenderer::ClearCanvas(SkColor color) {
174  // SkCanvas::clear doesn't respect the current clipping region
175  // so we SkCanvas::drawColor instead if scissoring is active.
176  if (is_scissor_enabled_)
177    current_canvas_->drawColor(color, SkXfermode::kSrc_Mode);
178  else
179    current_canvas_->clear(color);
180}
181
182void SoftwareRenderer::ClearFramebuffer(DrawingFrame* frame) {
183  if (frame->current_render_pass->has_transparent_background) {
184    ClearCanvas(SkColorSetARGB(0, 0, 0, 0));
185  } else {
186#ifndef NDEBUG
187    // On DEBUG builds, opaque render passes are cleared to blue
188    // to easily see regions that were not drawn on the screen.
189    ClearCanvas(SkColorSetARGB(255, 0, 0, 255));
190#endif
191  }
192}
193
194void SoftwareRenderer::SetDrawViewport(gfx::Rect window_space_viewport) {}
195
196bool SoftwareRenderer::IsSoftwareResource(
197    ResourceProvider::ResourceId resource_id) const {
198  switch (resource_provider_->GetResourceType(resource_id)) {
199    case ResourceProvider::GLTexture:
200      return false;
201    case ResourceProvider::Bitmap:
202      return true;
203    case ResourceProvider::InvalidType:
204      break;
205  }
206
207  LOG(FATAL) << "Invalid resource type.";
208  return false;
209}
210
211void SoftwareRenderer::DoDrawQuad(DrawingFrame* frame, const DrawQuad* quad) {
212  TRACE_EVENT0("cc", "SoftwareRenderer::DoDrawQuad");
213  gfx::Transform quad_rect_matrix;
214  QuadRectTransform(&quad_rect_matrix, quad->quadTransform(), quad->rect);
215  gfx::Transform contents_device_transform =
216      frame->window_matrix * frame->projection_matrix * quad_rect_matrix;
217  contents_device_transform.FlattenTo2d();
218  SkMatrix sk_device_matrix;
219  gfx::TransformToFlattenedSkMatrix(contents_device_transform,
220                                    &sk_device_matrix);
221  current_canvas_->setMatrix(sk_device_matrix);
222
223  current_paint_.reset();
224  if (!IsScaleAndIntegerTranslate(sk_device_matrix)) {
225    // TODO(danakj): Until we can enable AA only on exterior edges of the
226    // layer, disable AA if any interior edges are present. crbug.com/248175
227    bool all_four_edges_are_exterior = quad->IsTopEdge() &&
228                                       quad->IsLeftEdge() &&
229                                       quad->IsBottomEdge() &&
230                                       quad->IsRightEdge();
231    if (Settings().allow_antialiasing &&
232        all_four_edges_are_exterior)
233      current_paint_.setAntiAlias(true);
234    current_paint_.setFilterBitmap(true);
235  }
236
237  if (quad->ShouldDrawWithBlending()) {
238    current_paint_.setAlpha(quad->opacity() * 255);
239    current_paint_.setXfermodeMode(SkXfermode::kSrcOver_Mode);
240  } else {
241    current_paint_.setXfermodeMode(SkXfermode::kSrc_Mode);
242  }
243
244  switch (quad->material) {
245    case DrawQuad::CHECKERBOARD:
246      DrawCheckerboardQuad(frame, CheckerboardDrawQuad::MaterialCast(quad));
247      break;
248    case DrawQuad::DEBUG_BORDER:
249      DrawDebugBorderQuad(frame, DebugBorderDrawQuad::MaterialCast(quad));
250      break;
251    case DrawQuad::PICTURE_CONTENT:
252      DrawPictureQuad(frame, PictureDrawQuad::MaterialCast(quad));
253      break;
254    case DrawQuad::RENDER_PASS:
255      DrawRenderPassQuad(frame, RenderPassDrawQuad::MaterialCast(quad));
256      break;
257    case DrawQuad::SOLID_COLOR:
258      DrawSolidColorQuad(frame, SolidColorDrawQuad::MaterialCast(quad));
259      break;
260    case DrawQuad::TEXTURE_CONTENT:
261      DrawTextureQuad(frame, TextureDrawQuad::MaterialCast(quad));
262      break;
263    case DrawQuad::TILED_CONTENT:
264      DrawTileQuad(frame, TileDrawQuad::MaterialCast(quad));
265      break;
266    case DrawQuad::INVALID:
267    case DrawQuad::IO_SURFACE_CONTENT:
268    case DrawQuad::YUV_VIDEO_CONTENT:
269    case DrawQuad::STREAM_VIDEO_CONTENT:
270      DrawUnsupportedQuad(frame, quad);
271      NOTREACHED();
272      break;
273  }
274
275  current_canvas_->resetMatrix();
276}
277
278void SoftwareRenderer::DrawCheckerboardQuad(const DrawingFrame* frame,
279                                            const CheckerboardDrawQuad* quad) {
280  current_paint_.setColor(quad->color);
281  current_paint_.setAlpha(quad->opacity() * SkColorGetA(quad->color));
282  current_canvas_->drawRect(gfx::RectFToSkRect(QuadVertexRect()),
283                            current_paint_);
284}
285
286void SoftwareRenderer::DrawDebugBorderQuad(const DrawingFrame* frame,
287                                           const DebugBorderDrawQuad* quad) {
288  // We need to apply the matrix manually to have pixel-sized stroke width.
289  SkPoint vertices[4];
290  gfx::RectFToSkRect(QuadVertexRect()).toQuad(vertices);
291  SkPoint transformed_vertices[4];
292  current_canvas_->getTotalMatrix().mapPoints(transformed_vertices,
293                                              vertices,
294                                              4);
295  current_canvas_->resetMatrix();
296
297  current_paint_.setColor(quad->color);
298  current_paint_.setAlpha(quad->opacity() * SkColorGetA(quad->color));
299  current_paint_.setStyle(SkPaint::kStroke_Style);
300  current_paint_.setStrokeWidth(quad->width);
301  current_canvas_->drawPoints(SkCanvas::kPolygon_PointMode,
302                              4, transformed_vertices, current_paint_);
303}
304
305void SoftwareRenderer::DrawPictureQuad(const DrawingFrame* frame,
306                                       const PictureDrawQuad* quad) {
307  SkMatrix content_matrix;
308  content_matrix.setRectToRect(
309      gfx::RectFToSkRect(quad->tex_coord_rect),
310      gfx::RectFToSkRect(QuadVertexRect()),
311      SkMatrix::kFill_ScaleToFit);
312  current_canvas_->concat(content_matrix);
313
314  // TODO(aelias): This isn't correct in all cases. We should detect these
315  // cases and fall back to a persistent bitmap backing
316  // (http://crbug.com/280374).
317  skia::RefPtr<SkDrawFilter> opacity_filter =
318      skia::AdoptRef(new skia::OpacityDrawFilter(
319          quad->opacity(), frame->disable_picture_quad_image_filtering));
320  DCHECK(!current_canvas_->getDrawFilter());
321  current_canvas_->setDrawFilter(opacity_filter.get());
322
323  TRACE_EVENT0("cc",
324               "SoftwareRenderer::DrawPictureQuad");
325  quad->picture_pile->RasterDirect(
326      current_canvas_, quad->content_rect, quad->contents_scale, NULL);
327
328  current_canvas_->setDrawFilter(NULL);
329}
330
331void SoftwareRenderer::DrawSolidColorQuad(const DrawingFrame* frame,
332                                          const SolidColorDrawQuad* quad) {
333  current_paint_.setColor(quad->color);
334  current_paint_.setAlpha(quad->opacity() * SkColorGetA(quad->color));
335  current_canvas_->drawRect(gfx::RectFToSkRect(QuadVertexRect()),
336                            current_paint_);
337}
338
339void SoftwareRenderer::DrawTextureQuad(const DrawingFrame* frame,
340                                       const TextureDrawQuad* quad) {
341  if (!IsSoftwareResource(quad->resource_id)) {
342    DrawUnsupportedQuad(frame, quad);
343    return;
344  }
345
346  // TODO(skaslev): Add support for non-premultiplied alpha.
347  ResourceProvider::ScopedReadLockSoftware lock(resource_provider_,
348                                                quad->resource_id);
349  const SkBitmap* bitmap = lock.sk_bitmap();
350  gfx::RectF uv_rect = gfx::ScaleRect(gfx::BoundingRect(quad->uv_top_left,
351                                                        quad->uv_bottom_right),
352                                      bitmap->width(),
353                                      bitmap->height());
354  SkRect sk_uv_rect = gfx::RectFToSkRect(uv_rect);
355  SkRect quad_rect = gfx::RectFToSkRect(QuadVertexRect());
356
357  if (quad->flipped)
358    current_canvas_->scale(1, -1);
359
360  bool blend_background = quad->background_color != SK_ColorTRANSPARENT &&
361                          !bitmap->isOpaque();
362  bool needs_layer = blend_background && (current_paint_.getAlpha() != 0xFF);
363  if (needs_layer) {
364    current_canvas_->saveLayerAlpha(&quad_rect, current_paint_.getAlpha());
365    current_paint_.setAlpha(0xFF);
366  }
367  if (blend_background) {
368    SkPaint background_paint;
369    background_paint.setColor(quad->background_color);
370    current_canvas_->drawRect(quad_rect, background_paint);
371  }
372
373  current_canvas_->drawBitmapRectToRect(*bitmap,
374                                        &sk_uv_rect,
375                                        quad_rect,
376                                        &current_paint_);
377
378  if (needs_layer)
379    current_canvas_->restore();
380}
381
382void SoftwareRenderer::DrawTileQuad(const DrawingFrame* frame,
383                                    const TileDrawQuad* quad) {
384  DCHECK(!output_surface_->ForcedDrawToSoftwareDevice());
385  DCHECK(IsSoftwareResource(quad->resource_id));
386  ResourceProvider::ScopedReadLockSoftware lock(resource_provider_,
387                                                quad->resource_id);
388
389  SkRect uv_rect = gfx::RectFToSkRect(quad->tex_coord_rect);
390  current_paint_.setFilterBitmap(true);
391  current_canvas_->drawBitmapRectToRect(*lock.sk_bitmap(), &uv_rect,
392                                        gfx::RectFToSkRect(QuadVertexRect()),
393                                        &current_paint_);
394}
395
396void SoftwareRenderer::DrawRenderPassQuad(const DrawingFrame* frame,
397                                          const RenderPassDrawQuad* quad) {
398  CachedResource* content_texture =
399      render_pass_textures_.get(quad->render_pass_id);
400  if (!content_texture || !content_texture->id())
401    return;
402
403  DCHECK(IsSoftwareResource(content_texture->id()));
404  ResourceProvider::ScopedReadLockSoftware lock(resource_provider_,
405                                                content_texture->id());
406
407  SkRect dest_rect = gfx::RectFToSkRect(QuadVertexRect());
408  SkRect content_rect = SkRect::MakeWH(quad->rect.width(), quad->rect.height());
409
410  SkMatrix content_mat;
411  content_mat.setRectToRect(content_rect, dest_rect,
412                            SkMatrix::kFill_ScaleToFit);
413
414  const SkBitmap* content = lock.sk_bitmap();
415  skia::RefPtr<SkShader> shader = skia::AdoptRef(
416      SkShader::CreateBitmapShader(*content,
417                                   SkShader::kClamp_TileMode,
418                                   SkShader::kClamp_TileMode));
419  shader->setLocalMatrix(content_mat);
420  current_paint_.setShader(shader.get());
421
422  SkImageFilter* filter = quad->filter.get();
423  if (filter)
424    current_paint_.setImageFilter(filter);
425
426  if (quad->mask_resource_id) {
427    ResourceProvider::ScopedReadLockSoftware mask_lock(resource_provider_,
428                                                       quad->mask_resource_id);
429
430    const SkBitmap* mask = mask_lock.sk_bitmap();
431
432    SkRect mask_rect = SkRect::MakeXYWH(
433        quad->mask_uv_rect.x() * mask->width(),
434        quad->mask_uv_rect.y() * mask->height(),
435        quad->mask_uv_rect.width() * mask->width(),
436        quad->mask_uv_rect.height() * mask->height());
437
438    SkMatrix mask_mat;
439    mask_mat.setRectToRect(mask_rect, dest_rect, SkMatrix::kFill_ScaleToFit);
440
441    skia::RefPtr<SkShader> mask_shader = skia::AdoptRef(
442        SkShader::CreateBitmapShader(*mask,
443                                     SkShader::kClamp_TileMode,
444                                     SkShader::kClamp_TileMode));
445    mask_shader->setLocalMatrix(mask_mat);
446
447    SkPaint mask_paint;
448    mask_paint.setShader(mask_shader.get());
449
450    skia::RefPtr<SkLayerRasterizer> mask_rasterizer =
451        skia::AdoptRef(new SkLayerRasterizer);
452    mask_rasterizer->addLayer(mask_paint);
453
454    current_paint_.setRasterizer(mask_rasterizer.get());
455    current_canvas_->drawRect(dest_rect, current_paint_);
456  } else {
457    // TODO(skaslev): Apply background filters and blend with content
458    current_canvas_->drawRect(dest_rect, current_paint_);
459  }
460}
461
462void SoftwareRenderer::DrawUnsupportedQuad(const DrawingFrame* frame,
463                                           const DrawQuad* quad) {
464#ifdef NDEBUG
465  current_paint_.setColor(SK_ColorWHITE);
466#else
467  current_paint_.setColor(SK_ColorMAGENTA);
468#endif
469  current_paint_.setAlpha(quad->opacity() * 255);
470  current_canvas_->drawRect(gfx::RectFToSkRect(QuadVertexRect()),
471                            current_paint_);
472}
473
474void SoftwareRenderer::CopyCurrentRenderPassToBitmap(
475    DrawingFrame* frame,
476    scoped_ptr<CopyOutputRequest> request) {
477  gfx::Rect copy_rect = frame->current_render_pass->output_rect;
478  if (request->has_area()) {
479    // Intersect with the request's area, positioned with its origin at the
480    // origin of the full copy_rect.
481    copy_rect.Intersect(request->area() - copy_rect.OffsetFromOrigin());
482  }
483  gfx::Rect window_copy_rect = MoveFromDrawToWindowSpace(copy_rect);
484
485  scoped_ptr<SkBitmap> bitmap(new SkBitmap);
486  bitmap->setConfig(SkBitmap::kARGB_8888_Config,
487                    window_copy_rect.width(),
488                    window_copy_rect.height());
489  current_canvas_->readPixels(
490      bitmap.get(), window_copy_rect.x(), window_copy_rect.y());
491
492  request->SendBitmapResult(bitmap.Pass());
493}
494
495void SoftwareRenderer::GetFramebufferPixels(void* pixels, gfx::Rect rect) {
496  TRACE_EVENT0("cc", "SoftwareRenderer::GetFramebufferPixels");
497  SkBitmap subset_bitmap;
498  rect += current_viewport_rect_.OffsetFromOrigin();
499  output_device_->CopyToBitmap(rect, &subset_bitmap);
500  subset_bitmap.copyPixelsTo(pixels,
501                             4 * rect.width() * rect.height(),
502                             4 * rect.width());
503}
504
505void SoftwareRenderer::SetVisible(bool visible) {
506  if (visible_ == visible)
507    return;
508  visible_ = visible;
509}
510
511void SoftwareRenderer::SetDiscardBackBufferWhenNotVisible(bool discard) {
512  // TODO(piman, skaslev): Can we release the backbuffer? We don't currently
513  // receive memory policy yet anyway.
514  NOTIMPLEMENTED();
515}
516
517}  // namespace cc
518