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.
5 #include "cc/software_renderer.h"
7 #include <public/WebImage.h>
9 #include "base/debug/trace_event.h"
10 #include "cc/debug_border_draw_quad.h"
11 #include "cc/math_util.h"
12 #include "cc/render_pass_draw_quad.h"
13 #include "cc/software_output_device.h"
14 #include "cc/solid_color_draw_quad.h"
15 #include "cc/texture_draw_quad.h"
16 #include "cc/tile_draw_quad.h"
17 #include "third_party/skia/include/core/SkCanvas.h"
18 #include "third_party/skia/include/core/SkColor.h"
19 #include "third_party/skia/include/core/SkMatrix.h"
20 #include "third_party/skia/include/core/SkShader.h"
21 #include "third_party/skia/include/effects/SkLayerRasterizer.h"
22 #include "ui/gfx/rect_conversions.h"
23 #include "ui/gfx/skia_util.h"
24 #include "ui/gfx/transform.h"
30 void toSkMatrix(SkMatrix
* flattened
, const gfx::Transform
& m
)
32 // Convert from 4x4 to 3x3 by dropping the third row and column.
33 flattened
->set(0, SkDoubleToScalar(m
.matrix().getDouble(0, 0)));
34 flattened
->set(1, SkDoubleToScalar(m
.matrix().getDouble(0, 1)));
35 flattened
->set(2, SkDoubleToScalar(m
.matrix().getDouble(0, 3)));
36 flattened
->set(3, SkDoubleToScalar(m
.matrix().getDouble(1, 0)));
37 flattened
->set(4, SkDoubleToScalar(m
.matrix().getDouble(1, 1)));
38 flattened
->set(5, SkDoubleToScalar(m
.matrix().getDouble(1, 3)));
39 flattened
->set(6, SkDoubleToScalar(m
.matrix().getDouble(3, 0)));
40 flattened
->set(7, SkDoubleToScalar(m
.matrix().getDouble(3, 1)));
41 flattened
->set(8, SkDoubleToScalar(m
.matrix().getDouble(3, 3)));
44 bool isScaleAndTranslate(const SkMatrix
& matrix
)
46 return SkScalarNearlyZero(matrix
[SkMatrix::kMSkewX
]) &&
47 SkScalarNearlyZero(matrix
[SkMatrix::kMSkewY
]) &&
48 SkScalarNearlyZero(matrix
[SkMatrix::kMPersp0
]) &&
49 SkScalarNearlyZero(matrix
[SkMatrix::kMPersp1
]) &&
50 SkScalarNearlyZero(matrix
[SkMatrix::kMPersp2
] - 1.0f
);
53 } // anonymous namespace
55 scoped_ptr
<SoftwareRenderer
> SoftwareRenderer::create(RendererClient
* client
, ResourceProvider
* resourceProvider
, SoftwareOutputDevice
* outputDevice
)
57 return make_scoped_ptr(new SoftwareRenderer(client
, resourceProvider
, outputDevice
));
60 SoftwareRenderer::SoftwareRenderer(RendererClient
* client
, ResourceProvider
* resourceProvider
, SoftwareOutputDevice
* outputDevice
)
61 : DirectRenderer(client
, resourceProvider
)
63 , m_outputDevice(outputDevice
)
64 , m_skCurrentCanvas(0)
66 m_resourceProvider
->setDefaultResourceType(ResourceProvider::Bitmap
);
68 m_capabilities
.maxTextureSize
= m_resourceProvider
->maxTextureSize();
69 m_capabilities
.bestTextureFormat
= m_resourceProvider
->bestTextureFormat();
70 m_capabilities
.usingSetVisibility
= true;
71 // The updater can access bitmaps while the SoftwareRenderer is using them.
72 m_capabilities
.allowPartialTextureUpdates
= true;
77 SoftwareRenderer::~SoftwareRenderer()
81 const RendererCapabilities
& SoftwareRenderer::capabilities() const
83 return m_capabilities
;
86 void SoftwareRenderer::viewportChanged()
88 m_outputDevice
->DidChangeViewportSize(viewportSize());
91 void SoftwareRenderer::beginDrawingFrame(DrawingFrame
& frame
)
93 TRACE_EVENT0("cc", "SoftwareRenderer::beginDrawingFrame");
94 m_skRootCanvas
= make_scoped_ptr(new SkCanvas(m_outputDevice
->Lock(true)->getSkBitmap()));
97 void SoftwareRenderer::finishDrawingFrame(DrawingFrame
& frame
)
99 TRACE_EVENT0("cc", "SoftwareRenderer::finishDrawingFrame");
100 m_currentFramebufferLock
.reset();
101 m_skCurrentCanvas
= 0;
102 m_skRootCanvas
.reset();
103 m_outputDevice
->Unlock();
106 bool SoftwareRenderer::flippedFramebuffer() const
111 void SoftwareRenderer::ensureScissorTestEnabled()
113 // Nothing to do here. Current implementation of software rendering has no
114 // notion of enabling/disabling the feature.
117 void SoftwareRenderer::ensureScissorTestDisabled()
119 // There is no explicit notion of enabling/disabling scissoring in software
120 // rendering, but the underlying effect we want is to clear any existing
121 // clipRect on the current SkCanvas. This is done by setting clipRect to
122 // the viewport's dimensions.
123 SkISize canvasSize
= m_skCurrentCanvas
->getDeviceSize();
124 SkRect canvasRect
= SkRect::MakeXYWH(0, 0, canvasSize
.width(), canvasSize
.height());
125 m_skCurrentCanvas
->clipRect(canvasRect
, SkRegion::kReplace_Op
);
128 void SoftwareRenderer::finish()
132 void SoftwareRenderer::bindFramebufferToOutputSurface(DrawingFrame
& frame
)
134 m_currentFramebufferLock
.reset();
135 m_skCurrentCanvas
= m_skRootCanvas
.get();
138 bool SoftwareRenderer::bindFramebufferToTexture(DrawingFrame
& frame
, const ScopedResource
* texture
, const gfx::Rect
& framebufferRect
)
140 m_currentFramebufferLock
= make_scoped_ptr(new ResourceProvider::ScopedWriteLockSoftware(m_resourceProvider
, texture
->id()));
141 m_skCurrentCanvas
= m_currentFramebufferLock
->skCanvas();
142 initializeMatrices(frame
, framebufferRect
, false);
143 setDrawViewportSize(framebufferRect
.size());
148 void SoftwareRenderer::setScissorTestRect(const gfx::Rect
& scissorRect
)
150 m_skCurrentCanvas
->clipRect(gfx::RectToSkRect(scissorRect
), SkRegion::kReplace_Op
);
153 void SoftwareRenderer::clearFramebuffer(DrawingFrame
& frame
)
155 if (frame
.currentRenderPass
->has_transparent_background
) {
156 m_skCurrentCanvas
->clear(SkColorSetARGB(0, 0, 0, 0));
159 // On DEBUG builds, opaque render passes are cleared to blue to easily see regions that were not drawn on the screen.
160 m_skCurrentCanvas
->clear(SkColorSetARGB(255, 0, 0, 255));
165 void SoftwareRenderer::setDrawViewportSize(const gfx::Size
& viewportSize
)
169 bool SoftwareRenderer::isSoftwareResource(ResourceProvider::ResourceId id
) const
171 switch (m_resourceProvider
->resourceType(id
)) {
172 case ResourceProvider::GLTexture
:
174 case ResourceProvider::Bitmap
:
178 LOG(FATAL
) << "Invalid resource type.";
182 void SoftwareRenderer::drawQuad(DrawingFrame
& frame
, const DrawQuad
* quad
)
184 TRACE_EVENT0("cc", "SoftwareRenderer::drawQuad");
185 gfx::Transform quadRectMatrix
;
186 quadRectTransform(&quadRectMatrix
, quad
->quadTransform(), quad
->rect
);
187 gfx::Transform contentsDeviceTransform
= MathUtil::to2dTransform(frame
.windowMatrix
* frame
.projectionMatrix
* quadRectMatrix
);
188 SkMatrix skDeviceMatrix
;
189 toSkMatrix(&skDeviceMatrix
, contentsDeviceTransform
);
190 m_skCurrentCanvas
->setMatrix(skDeviceMatrix
);
192 m_skCurrentPaint
.reset();
193 if (!isScaleAndTranslate(skDeviceMatrix
)) {
194 m_skCurrentPaint
.setAntiAlias(true);
195 m_skCurrentPaint
.setFilterBitmap(true);
198 if (quad
->ShouldDrawWithBlending()) {
199 m_skCurrentPaint
.setAlpha(quad
->opacity() * 255);
200 m_skCurrentPaint
.setXfermodeMode(SkXfermode::kSrcOver_Mode
);
202 m_skCurrentPaint
.setXfermodeMode(SkXfermode::kSrc_Mode
);
205 switch (quad
->material
) {
206 case DrawQuad::DEBUG_BORDER
:
207 drawDebugBorderQuad(frame
, DebugBorderDrawQuad::MaterialCast(quad
));
209 case DrawQuad::SOLID_COLOR
:
210 drawSolidColorQuad(frame
, SolidColorDrawQuad::MaterialCast(quad
));
212 case DrawQuad::TEXTURE_CONTENT
:
213 drawTextureQuad(frame
, TextureDrawQuad::MaterialCast(quad
));
215 case DrawQuad::TILED_CONTENT
:
216 drawTileQuad(frame
, TileDrawQuad::MaterialCast(quad
));
218 case DrawQuad::RENDER_PASS
:
219 drawRenderPassQuad(frame
, RenderPassDrawQuad::MaterialCast(quad
));
222 drawUnsupportedQuad(frame
, quad
);
226 m_skCurrentCanvas
->resetMatrix();
229 void SoftwareRenderer::drawDebugBorderQuad(const DrawingFrame
& frame
, const DebugBorderDrawQuad
* quad
)
231 // We need to apply the matrix manually to have pixel-sized stroke width.
233 gfx::RectFToSkRect(quadVertexRect()).toQuad(vertices
);
234 SkPoint transformedVertices
[4];
235 m_skCurrentCanvas
->getTotalMatrix().mapPoints(transformedVertices
, vertices
, 4);
236 m_skCurrentCanvas
->resetMatrix();
238 m_skCurrentPaint
.setColor(quad
->color
);
239 m_skCurrentPaint
.setAlpha(quad
->opacity() * SkColorGetA(quad
->color
));
240 m_skCurrentPaint
.setStyle(SkPaint::kStroke_Style
);
241 m_skCurrentPaint
.setStrokeWidth(quad
->width
);
242 m_skCurrentCanvas
->drawPoints(SkCanvas::kPolygon_PointMode
, 4, transformedVertices
, m_skCurrentPaint
);
245 void SoftwareRenderer::drawSolidColorQuad(const DrawingFrame
& frame
, const SolidColorDrawQuad
* quad
)
247 m_skCurrentPaint
.setColor(quad
->color
);
248 m_skCurrentPaint
.setAlpha(quad
->opacity() * SkColorGetA(quad
->color
));
249 m_skCurrentCanvas
->drawRect(gfx::RectFToSkRect(quadVertexRect()), m_skCurrentPaint
);
252 void SoftwareRenderer::drawTextureQuad(const DrawingFrame
& frame
, const TextureDrawQuad
* quad
)
254 if (!isSoftwareResource(quad
->resource_id
)) {
255 drawUnsupportedQuad(frame
, quad
);
259 // FIXME: Add support for non-premultiplied alpha.
260 ResourceProvider::ScopedReadLockSoftware
lock(m_resourceProvider
, quad
->resource_id
);
261 const SkBitmap
* bitmap
= lock
.skBitmap();
262 gfx::RectF uvRect
= gfx::ScaleRect(quad
->uv_rect
, bitmap
->width(), bitmap
->height());
263 SkRect skUvRect
= gfx::RectFToSkRect(uvRect
);
265 m_skCurrentCanvas
->scale(1, -1);
266 m_skCurrentCanvas
->drawBitmapRectToRect(*bitmap
, &skUvRect
,
267 gfx::RectFToSkRect(quadVertexRect()),
271 void SoftwareRenderer::drawTileQuad(const DrawingFrame
& frame
, const TileDrawQuad
* quad
)
273 DCHECK(isSoftwareResource(quad
->resource_id
));
274 ResourceProvider::ScopedReadLockSoftware
lock(m_resourceProvider
, quad
->resource_id
);
276 SkRect uvRect
= gfx::RectFToSkRect(quad
->tex_coord_rect
);
277 m_skCurrentPaint
.setFilterBitmap(true);
278 m_skCurrentCanvas
->drawBitmapRectToRect(*lock
.skBitmap(), &uvRect
,
279 gfx::RectFToSkRect(quadVertexRect()),
283 void SoftwareRenderer::drawRenderPassQuad(const DrawingFrame
& frame
, const RenderPassDrawQuad
* quad
)
285 CachedResource
* contentTexture
= m_renderPassTextures
.get(quad
->render_pass_id
);
286 if (!contentTexture
|| !contentTexture
->id())
289 const RenderPass
* renderPass
= frame
.renderPassesById
->get(quad
->render_pass_id
);
294 DCHECK(isSoftwareResource(contentTexture
->id()));
295 ResourceProvider::ScopedReadLockSoftware
lock(m_resourceProvider
, contentTexture
->id());
297 SkRect destRect
= gfx::RectFToSkRect(quadVertexRect());
299 const SkBitmap
* content
= lock
.skBitmap();
302 content
->getBounds(&contentRect
);
305 contentMat
.setRectToRect(contentRect
, destRect
, SkMatrix::kFill_ScaleToFit
);
307 skia::RefPtr
<SkShader
> shader
= skia::AdoptRef(
308 SkShader::CreateBitmapShader(*content
,
309 SkShader::kClamp_TileMode
,
310 SkShader::kClamp_TileMode
));
311 shader
->setLocalMatrix(contentMat
);
312 m_skCurrentPaint
.setShader(shader
.get());
314 SkImageFilter
* filter
= renderPass
->filter
.get();
316 m_skCurrentPaint
.setImageFilter(filter
);
318 if (quad
->mask_resource_id
) {
319 ResourceProvider::ScopedReadLockSoftware
maskLock(m_resourceProvider
, quad
->mask_resource_id
);
321 const SkBitmap
* mask
= maskLock
.skBitmap();
323 SkRect maskRect
= SkRect::MakeXYWH(
324 quad
->mask_uv_rect
.x() * mask
->width(),
325 quad
->mask_uv_rect
.y() * mask
->height(),
326 quad
->mask_uv_rect
.width() * mask
->width(),
327 quad
->mask_uv_rect
.height() * mask
->height());
330 maskMat
.setRectToRect(maskRect
, destRect
, SkMatrix::kFill_ScaleToFit
);
332 skia::RefPtr
<SkShader
> maskShader
= skia::AdoptRef(
333 SkShader::CreateBitmapShader(*mask
,
334 SkShader::kClamp_TileMode
,
335 SkShader::kClamp_TileMode
));
336 maskShader
->setLocalMatrix(maskMat
);
339 maskPaint
.setShader(maskShader
.get());
341 skia::RefPtr
<SkLayerRasterizer
> maskRasterizer
= skia::AdoptRef(new SkLayerRasterizer
);
342 maskRasterizer
->addLayer(maskPaint
);
344 m_skCurrentPaint
.setRasterizer(maskRasterizer
.get());
345 m_skCurrentCanvas
->drawRect(destRect
, m_skCurrentPaint
);
347 // FIXME: Apply background filters and blend with content
348 m_skCurrentCanvas
->drawRect(destRect
, m_skCurrentPaint
);
352 void SoftwareRenderer::drawUnsupportedQuad(const DrawingFrame
& frame
, const DrawQuad
* quad
)
354 m_skCurrentPaint
.setColor(SK_ColorMAGENTA
);
355 m_skCurrentPaint
.setAlpha(quad
->opacity() * 255);
356 m_skCurrentCanvas
->drawRect(gfx::RectFToSkRect(quadVertexRect()), m_skCurrentPaint
);
359 bool SoftwareRenderer::swapBuffers()
361 if (m_client
->hasImplThread())
362 m_client
->onSwapBuffersComplete();
366 void SoftwareRenderer::getFramebufferPixels(void *pixels
, const gfx::Rect
& rect
)
368 TRACE_EVENT0("cc", "SoftwareRenderer::getFramebufferPixels");
369 SkBitmap fullBitmap
= m_outputDevice
->Lock(false)->getSkBitmap();
370 SkBitmap subsetBitmap
;
371 SkIRect invertRect
= SkIRect::MakeXYWH(rect
.x(), viewportSize().height() - rect
.bottom(), rect
.width(), rect
.height());
372 fullBitmap
.extractSubset(&subsetBitmap
, invertRect
);
373 subsetBitmap
.copyPixelsTo(pixels
, rect
.width() * rect
.height() * 4, rect
.width() * 4);
374 m_outputDevice
->Unlock();
377 void SoftwareRenderer::setVisible(bool visible
)
379 if (m_visible
== visible
)