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 "base/debug/trace_event.h"
8 #include "cc/debug_border_draw_quad.h"
9 #include "cc/render_pass_draw_quad.h"
10 #include "cc/solid_color_draw_quad.h"
11 #include "cc/texture_draw_quad.h"
12 #include "cc/tile_draw_quad.h"
13 #include "third_party/skia/include/core/SkCanvas.h"
14 #include "third_party/skia/include/core/SkColor.h"
15 #include "third_party/skia/include/core/SkMatrix.h"
16 #include "third_party/skia/include/core/SkShader.h"
17 #include "third_party/skia/include/effects/SkLayerRasterizer.h"
18 #include "ui/gfx/rect_conversions.h"
19 #include "ui/gfx/skia_util.h"
20 #include <public/WebCompositorSoftwareOutputDevice.h>
21 #include <public/WebImage.h>
22 #include <public/WebSize.h>
23 #include <public/WebTransformationMatrix.h>
25 using WebKit::WebCompositorSoftwareOutputDevice
;
26 using WebKit::WebSize
;
27 using WebKit::WebTransformationMatrix
;
33 void toSkMatrix(SkMatrix
* flattened
, const WebTransformationMatrix
& m
)
35 // Convert from 4x4 to 3x3 by dropping the third row and column.
36 flattened
->set(0, SkDoubleToScalar(m
.m11()));
37 flattened
->set(1, SkDoubleToScalar(m
.m21()));
38 flattened
->set(2, SkDoubleToScalar(m
.m41()));
39 flattened
->set(3, SkDoubleToScalar(m
.m12()));
40 flattened
->set(4, SkDoubleToScalar(m
.m22()));
41 flattened
->set(5, SkDoubleToScalar(m
.m42()));
42 flattened
->set(6, SkDoubleToScalar(m
.m14()));
43 flattened
->set(7, SkDoubleToScalar(m
.m24()));
44 flattened
->set(8, SkDoubleToScalar(m
.m44()));
47 bool isScaleAndTranslate(const SkMatrix
& matrix
)
49 return SkScalarNearlyZero(matrix
[SkMatrix::kMSkewX
]) &&
50 SkScalarNearlyZero(matrix
[SkMatrix::kMSkewY
]) &&
51 SkScalarNearlyZero(matrix
[SkMatrix::kMPersp0
]) &&
52 SkScalarNearlyZero(matrix
[SkMatrix::kMPersp1
]) &&
53 SkScalarNearlyZero(matrix
[SkMatrix::kMPersp2
] - 1.0f
);
56 } // anonymous namespace
58 scoped_ptr
<SoftwareRenderer
> SoftwareRenderer::create(RendererClient
* client
, ResourceProvider
* resourceProvider
, WebCompositorSoftwareOutputDevice
* outputDevice
)
60 return make_scoped_ptr(new SoftwareRenderer(client
, resourceProvider
, outputDevice
));
63 SoftwareRenderer::SoftwareRenderer(RendererClient
* client
, ResourceProvider
* resourceProvider
, WebCompositorSoftwareOutputDevice
* outputDevice
)
64 : DirectRenderer(client
, resourceProvider
)
66 , m_outputDevice(outputDevice
)
67 , m_skCurrentCanvas(0)
69 m_resourceProvider
->setDefaultResourceType(ResourceProvider::Bitmap
);
71 m_capabilities
.maxTextureSize
= INT_MAX
;
72 m_capabilities
.bestTextureFormat
= GL_RGBA
;
73 m_capabilities
.contextHasCachedFrontBuffer
= true;
74 m_capabilities
.usingSetVisibility
= true;
79 SoftwareRenderer::~SoftwareRenderer()
83 const RendererCapabilities
& SoftwareRenderer::capabilities() const
85 return m_capabilities
;
88 void SoftwareRenderer::viewportChanged()
90 m_outputDevice
->didChangeViewportSize(WebSize(viewportSize().width(), viewportSize().height()));
93 void SoftwareRenderer::beginDrawingFrame(DrawingFrame
& frame
)
95 TRACE_EVENT0("cc", "SoftwareRenderer::beginDrawingFrame");
96 m_skRootCanvas
= make_scoped_ptr(new SkCanvas(m_outputDevice
->lock(true)->getSkBitmap()));
99 void SoftwareRenderer::finishDrawingFrame(DrawingFrame
& frame
)
101 TRACE_EVENT0("cc", "SoftwareRenderer::finishDrawingFrame");
102 m_currentFramebufferLock
.reset();
103 m_skCurrentCanvas
= 0;
104 m_skRootCanvas
.reset();
105 m_outputDevice
->unlock();
108 bool SoftwareRenderer::flippedFramebuffer() const
113 void SoftwareRenderer::finish()
117 void SoftwareRenderer::bindFramebufferToOutputSurface(DrawingFrame
& frame
)
119 m_currentFramebufferLock
.reset();
120 m_skCurrentCanvas
= m_skRootCanvas
.get();
123 bool SoftwareRenderer::bindFramebufferToTexture(DrawingFrame
& frame
, const ScopedResource
* texture
, const gfx::Rect
& framebufferRect
)
125 m_currentFramebufferLock
= make_scoped_ptr(new ResourceProvider::ScopedWriteLockSoftware(m_resourceProvider
, texture
->id()));
126 m_skCurrentCanvas
= m_currentFramebufferLock
->skCanvas();
127 initializeMatrices(frame
, framebufferRect
, false);
128 setDrawViewportSize(framebufferRect
.size());
133 void SoftwareRenderer::setScissorTestRect(const gfx::Rect
& scissorRect
)
135 m_skCurrentCanvas
->clipRect(gfx::RectToSkRect(scissorRect
), SkRegion::kReplace_Op
);
138 void SoftwareRenderer::clearFramebuffer(DrawingFrame
& frame
)
140 if (frame
.currentRenderPass
->hasTransparentBackground()) {
141 m_skCurrentCanvas
->clear(SkColorSetARGB(0, 0, 0, 0));
144 // On DEBUG builds, opaque render passes are cleared to blue to easily see regions that were not drawn on the screen.
145 m_skCurrentCanvas
->clear(SkColorSetARGB(255, 0, 0, 255));
150 void SoftwareRenderer::setDrawViewportSize(const gfx::Size
& viewportSize
)
154 bool SoftwareRenderer::isSoftwareResource(ResourceProvider::ResourceId id
) const
156 switch (m_resourceProvider
->resourceType(id
)) {
157 case ResourceProvider::GLTexture
:
159 case ResourceProvider::Bitmap
:
163 LOG(FATAL
) << "Invalid resource type.";
167 void SoftwareRenderer::drawQuad(DrawingFrame
& frame
, const DrawQuad
* quad
)
169 TRACE_EVENT0("cc", "SoftwareRenderer::drawQuad");
170 WebTransformationMatrix quadRectMatrix
;
171 quadRectTransform(&quadRectMatrix
, quad
->quadTransform(), quad
->quadRect());
172 WebTransformationMatrix contentsDeviceTransform
= (frame
.windowMatrix
* frame
.projectionMatrix
* quadRectMatrix
).to2dTransform();
173 SkMatrix skDeviceMatrix
;
174 toSkMatrix(&skDeviceMatrix
, contentsDeviceTransform
);
175 m_skCurrentCanvas
->setMatrix(skDeviceMatrix
);
177 m_skCurrentPaint
.reset();
178 if (!isScaleAndTranslate(skDeviceMatrix
)) {
179 m_skCurrentPaint
.setAntiAlias(true);
180 m_skCurrentPaint
.setFilterBitmap(true);
182 if (quad
->needsBlending()) {
183 m_skCurrentPaint
.setAlpha(quad
->opacity() * 255);
184 m_skCurrentPaint
.setXfermodeMode(SkXfermode::kSrcOver_Mode
);
186 m_skCurrentPaint
.setXfermodeMode(SkXfermode::kSrc_Mode
);
189 switch (quad
->material()) {
190 case DrawQuad::DebugBorder
:
191 drawDebugBorderQuad(frame
, DebugBorderDrawQuad::materialCast(quad
));
193 case DrawQuad::SolidColor
:
194 drawSolidColorQuad(frame
, SolidColorDrawQuad::materialCast(quad
));
196 case DrawQuad::TextureContent
:
197 drawTextureQuad(frame
, TextureDrawQuad::materialCast(quad
));
199 case DrawQuad::TiledContent
:
200 drawTileQuad(frame
, TileDrawQuad::materialCast(quad
));
202 case DrawQuad::RenderPass
:
203 drawRenderPassQuad(frame
, RenderPassDrawQuad::materialCast(quad
));
206 drawUnsupportedQuad(frame
, quad
);
210 m_skCurrentCanvas
->resetMatrix();
213 void SoftwareRenderer::drawDebugBorderQuad(const DrawingFrame
& frame
, const DebugBorderDrawQuad
* quad
)
215 // We need to apply the matrix manually to have pixel-sized stroke width.
217 gfx::RectFToSkRect(quadVertexRect()).toQuad(vertices
);
218 SkPoint transformedVertices
[4];
219 m_skCurrentCanvas
->getTotalMatrix().mapPoints(transformedVertices
, vertices
, 4);
220 m_skCurrentCanvas
->resetMatrix();
222 m_skCurrentPaint
.setColor(quad
->color());
223 m_skCurrentPaint
.setAlpha(quad
->opacity() * SkColorGetA(quad
->color()));
224 m_skCurrentPaint
.setStyle(SkPaint::kStroke_Style
);
225 m_skCurrentPaint
.setStrokeWidth(quad
->width());
226 m_skCurrentCanvas
->drawPoints(SkCanvas::kPolygon_PointMode
, 4, transformedVertices
, m_skCurrentPaint
);
229 void SoftwareRenderer::drawSolidColorQuad(const DrawingFrame
& frame
, const SolidColorDrawQuad
* quad
)
231 m_skCurrentPaint
.setColor(quad
->color());
232 m_skCurrentPaint
.setAlpha(quad
->opacity() * SkColorGetA(quad
->color()));
233 m_skCurrentCanvas
->drawRect(gfx::RectFToSkRect(quadVertexRect()), m_skCurrentPaint
);
236 void SoftwareRenderer::drawTextureQuad(const DrawingFrame
& frame
, const TextureDrawQuad
* quad
)
238 if (!isSoftwareResource(quad
->resourceId())) {
239 drawUnsupportedQuad(frame
, quad
);
243 // FIXME: Add support for non-premultiplied alpha.
244 ResourceProvider::ScopedReadLockSoftware
lock(m_resourceProvider
, quad
->resourceId());
245 const SkBitmap
* bitmap
= lock
.skBitmap();
246 gfx::RectF uvRect
= gfx::ScaleRect(quad
->uvRect(), bitmap
->width(), bitmap
->height());
247 SkRect skUvRect
= gfx::RectFToSkRect(uvRect
);
249 m_skCurrentCanvas
->scale(1, -1);
250 m_skCurrentCanvas
->drawBitmapRectToRect(*bitmap
, &skUvRect
,
251 gfx::RectFToSkRect(quadVertexRect()),
255 void SoftwareRenderer::drawTileQuad(const DrawingFrame
& frame
, const TileDrawQuad
* quad
)
257 DCHECK(isSoftwareResource(quad
->resourceId()));
258 ResourceProvider::ScopedReadLockSoftware
lock(m_resourceProvider
, quad
->resourceId());
260 SkRect uvRect
= SkRect::MakeXYWH(
261 quad
->textureOffset().x(), quad
->textureOffset().y(),
262 quad
->quadRect().width(), quad
->quadRect().height());
263 m_skCurrentPaint
.setFilterBitmap(true);
264 m_skCurrentCanvas
->drawBitmapRectToRect(*lock
.skBitmap(), &uvRect
,
265 gfx::RectFToSkRect(quadVertexRect()),
269 void SoftwareRenderer::drawRenderPassQuad(const DrawingFrame
& frame
, const RenderPassDrawQuad
* quad
)
271 CachedResource
* contentTexture
= m_renderPassTextures
.get(quad
->renderPassId());
272 if (!contentTexture
|| !contentTexture
->id())
275 const RenderPass
* renderPass
= frame
.renderPassesById
->get(quad
->renderPassId());
280 DCHECK(isSoftwareResource(contentTexture
->id()));
281 ResourceProvider::ScopedReadLockSoftware
lock(m_resourceProvider
, contentTexture
->id());
283 SkRect destRect
= gfx::RectFToSkRect(quadVertexRect());
285 const SkBitmap
* content
= lock
.skBitmap();
288 content
->getBounds(&contentRect
);
291 contentMat
.setRectToRect(contentRect
, destRect
, SkMatrix::kFill_ScaleToFit
);
293 SkAutoTUnref
<SkShader
> shader(SkShader::CreateBitmapShader(*content
,
294 SkShader::kClamp_TileMode
,
295 SkShader::kClamp_TileMode
));
296 shader
->setLocalMatrix(contentMat
);
297 m_skCurrentPaint
.setShader(shader
);
299 SkImageFilter
* filter
= renderPass
->filter();
301 m_skCurrentPaint
.setImageFilter(filter
);
303 if (quad
->maskResourceId()) {
304 ResourceProvider::ScopedReadLockSoftware
maskLock(m_resourceProvider
, quad
->maskResourceId());
306 const SkBitmap
* mask
= maskLock
.skBitmap();
308 SkRect maskRect
= SkRect::MakeXYWH(
309 quad
->maskTexCoordOffsetX() * mask
->width(),
310 quad
->maskTexCoordOffsetY() * mask
->height(),
311 quad
->maskTexCoordScaleX() * mask
->width(),
312 quad
->maskTexCoordScaleY() * mask
->height());
315 maskMat
.setRectToRect(maskRect
, destRect
, SkMatrix::kFill_ScaleToFit
);
317 SkAutoTUnref
<SkShader
> maskShader(SkShader::CreateBitmapShader(*mask
,
318 SkShader::kClamp_TileMode
,
319 SkShader::kClamp_TileMode
));
320 maskShader
->setLocalMatrix(maskMat
);
323 maskPaint
.setShader(maskShader
);
325 SkAutoTUnref
<SkLayerRasterizer
> maskRasterizer(new SkLayerRasterizer
);
326 maskRasterizer
->addLayer(maskPaint
);
328 m_skCurrentPaint
.setRasterizer(maskRasterizer
);
329 m_skCurrentCanvas
->drawRect(destRect
, m_skCurrentPaint
);
331 // FIXME: Apply background filters and blend with content
332 m_skCurrentCanvas
->drawRect(destRect
, m_skCurrentPaint
);
336 void SoftwareRenderer::drawUnsupportedQuad(const DrawingFrame
& frame
, const DrawQuad
* quad
)
338 m_skCurrentPaint
.setColor(SK_ColorMAGENTA
);
339 m_skCurrentPaint
.setAlpha(quad
->opacity() * 255);
340 m_skCurrentCanvas
->drawRect(gfx::RectFToSkRect(quadVertexRect()), m_skCurrentPaint
);
343 bool SoftwareRenderer::swapBuffers()
345 if (m_client
->hasImplThread())
346 m_client
->onSwapBuffersComplete();
350 void SoftwareRenderer::getFramebufferPixels(void *pixels
, const gfx::Rect
& rect
)
352 TRACE_EVENT0("cc", "SoftwareRenderer::getFramebufferPixels");
353 SkBitmap fullBitmap
= m_outputDevice
->lock(false)->getSkBitmap();
354 SkBitmap subsetBitmap
;
355 SkIRect invertRect
= SkIRect::MakeXYWH(rect
.x(), viewportSize().height() - rect
.bottom(), rect
.width(), rect
.height());
356 fullBitmap
.extractSubset(&subsetBitmap
, invertRect
);
357 subsetBitmap
.copyPixelsTo(pixels
, rect
.width() * rect
.height() * 4, rect
.width() * 4);
358 m_outputDevice
->unlock();
361 void SoftwareRenderer::setVisible(bool visible
)
363 if (m_visible
== visible
)