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/math_util.h"
10 #include "cc/render_pass_draw_quad.h"
11 #include "cc/software_output_device.h"
12 #include "cc/solid_color_draw_quad.h"
13 #include "cc/texture_draw_quad.h"
14 #include "cc/tile_draw_quad.h"
15 #include "third_party/WebKit/Source/Platform/chromium/public/WebImage.h"
16 #include "third_party/skia/include/core/SkCanvas.h"
17 #include "third_party/skia/include/core/SkColor.h"
18 #include "third_party/skia/include/core/SkMatrix.h"
19 #include "third_party/skia/include/core/SkShader.h"
20 #include "third_party/skia/include/effects/SkLayerRasterizer.h"
21 #include "ui/gfx/rect_conversions.h"
22 #include "ui/gfx/skia_util.h"
23 #include "ui/gfx/transform.h"
29 void toSkMatrix(SkMatrix
* flattened
, const gfx::Transform
& m
)
31 // Convert from 4x4 to 3x3 by dropping the third row and column.
32 flattened
->set(0, SkDoubleToScalar(m
.matrix().getDouble(0, 0)));
33 flattened
->set(1, SkDoubleToScalar(m
.matrix().getDouble(0, 1)));
34 flattened
->set(2, SkDoubleToScalar(m
.matrix().getDouble(0, 3)));
35 flattened
->set(3, SkDoubleToScalar(m
.matrix().getDouble(1, 0)));
36 flattened
->set(4, SkDoubleToScalar(m
.matrix().getDouble(1, 1)));
37 flattened
->set(5, SkDoubleToScalar(m
.matrix().getDouble(1, 3)));
38 flattened
->set(6, SkDoubleToScalar(m
.matrix().getDouble(3, 0)));
39 flattened
->set(7, SkDoubleToScalar(m
.matrix().getDouble(3, 1)));
40 flattened
->set(8, SkDoubleToScalar(m
.matrix().getDouble(3, 3)));
43 bool isScaleAndTranslate(const SkMatrix
& matrix
)
45 return SkScalarNearlyZero(matrix
[SkMatrix::kMSkewX
]) &&
46 SkScalarNearlyZero(matrix
[SkMatrix::kMSkewY
]) &&
47 SkScalarNearlyZero(matrix
[SkMatrix::kMPersp0
]) &&
48 SkScalarNearlyZero(matrix
[SkMatrix::kMPersp1
]) &&
49 SkScalarNearlyZero(matrix
[SkMatrix::kMPersp2
] - 1.0f
);
52 } // anonymous namespace
54 scoped_ptr
<SoftwareRenderer
> SoftwareRenderer::create(RendererClient
* client
, ResourceProvider
* resourceProvider
, SoftwareOutputDevice
* outputDevice
)
56 return make_scoped_ptr(new SoftwareRenderer(client
, resourceProvider
, outputDevice
));
59 SoftwareRenderer::SoftwareRenderer(RendererClient
* client
, ResourceProvider
* resourceProvider
, SoftwareOutputDevice
* outputDevice
)
60 : DirectRenderer(client
, resourceProvider
)
62 , m_outputDevice(outputDevice
)
63 , m_skCurrentCanvas(0)
65 m_resourceProvider
->setDefaultResourceType(ResourceProvider::Bitmap
);
67 m_capabilities
.maxTextureSize
= m_resourceProvider
->maxTextureSize();
68 m_capabilities
.bestTextureFormat
= m_resourceProvider
->bestTextureFormat();
69 m_capabilities
.usingSetVisibility
= true;
70 // The updater can access bitmaps while the SoftwareRenderer is using them.
71 m_capabilities
.allowPartialTextureUpdates
= true;
76 SoftwareRenderer::~SoftwareRenderer()
80 const RendererCapabilities
& SoftwareRenderer::capabilities() const
82 return m_capabilities
;
85 void SoftwareRenderer::viewportChanged()
87 m_outputDevice
->DidChangeViewportSize(viewportSize());
90 void SoftwareRenderer::beginDrawingFrame(DrawingFrame
& frame
)
92 TRACE_EVENT0("cc", "SoftwareRenderer::beginDrawingFrame");
93 m_skRootCanvas
= make_scoped_ptr(new SkCanvas(m_outputDevice
->Lock(true)->getSkBitmap()));
96 void SoftwareRenderer::finishDrawingFrame(DrawingFrame
& frame
)
98 TRACE_EVENT0("cc", "SoftwareRenderer::finishDrawingFrame");
99 m_currentFramebufferLock
.reset();
100 m_skCurrentCanvas
= 0;
101 m_skRootCanvas
.reset();
102 m_outputDevice
->Unlock();
105 bool SoftwareRenderer::flippedFramebuffer() const
110 void SoftwareRenderer::ensureScissorTestEnabled()
112 // Nothing to do here. Current implementation of software rendering has no
113 // notion of enabling/disabling the feature.
116 void SoftwareRenderer::ensureScissorTestDisabled()
118 // There is no explicit notion of enabling/disabling scissoring in software
119 // rendering, but the underlying effect we want is to clear any existing
120 // clipRect on the current SkCanvas. This is done by setting clipRect to
121 // the viewport's dimensions.
122 SkISize canvasSize
= m_skCurrentCanvas
->getDeviceSize();
123 SkRect canvasRect
= SkRect::MakeXYWH(0, 0, canvasSize
.width(), canvasSize
.height());
124 m_skCurrentCanvas
->clipRect(canvasRect
, SkRegion::kReplace_Op
);
127 void SoftwareRenderer::finish()
131 void SoftwareRenderer::bindFramebufferToOutputSurface(DrawingFrame
& frame
)
133 m_currentFramebufferLock
.reset();
134 m_skCurrentCanvas
= m_skRootCanvas
.get();
137 bool SoftwareRenderer::bindFramebufferToTexture(DrawingFrame
& frame
, const ScopedResource
* texture
, const gfx::Rect
& framebufferRect
)
139 m_currentFramebufferLock
= make_scoped_ptr(new ResourceProvider::ScopedWriteLockSoftware(m_resourceProvider
, texture
->id()));
140 m_skCurrentCanvas
= m_currentFramebufferLock
->skCanvas();
141 initializeMatrices(frame
, framebufferRect
, false);
142 setDrawViewportSize(framebufferRect
.size());
147 void SoftwareRenderer::setScissorTestRect(const gfx::Rect
& scissorRect
)
149 m_skCurrentCanvas
->clipRect(gfx::RectToSkRect(scissorRect
), SkRegion::kReplace_Op
);
152 void SoftwareRenderer::clearFramebuffer(DrawingFrame
& frame
)
154 if (frame
.currentRenderPass
->has_transparent_background
) {
155 m_skCurrentCanvas
->clear(SkColorSetARGB(0, 0, 0, 0));
158 // On DEBUG builds, opaque render passes are cleared to blue to easily see regions that were not drawn on the screen.
159 m_skCurrentCanvas
->clear(SkColorSetARGB(255, 0, 0, 255));
164 void SoftwareRenderer::setDrawViewportSize(const gfx::Size
& viewportSize
)
168 bool SoftwareRenderer::isSoftwareResource(ResourceProvider::ResourceId id
) const
170 switch (m_resourceProvider
->resourceType(id
)) {
171 case ResourceProvider::GLTexture
:
173 case ResourceProvider::Bitmap
:
177 LOG(FATAL
) << "Invalid resource type.";
181 void SoftwareRenderer::drawQuad(DrawingFrame
& frame
, const DrawQuad
* quad
)
183 TRACE_EVENT0("cc", "SoftwareRenderer::drawQuad");
184 gfx::Transform quadRectMatrix
;
185 quadRectTransform(&quadRectMatrix
, quad
->quadTransform(), quad
->rect
);
186 gfx::Transform contentsDeviceTransform
= MathUtil::to2dTransform(frame
.windowMatrix
* frame
.projectionMatrix
* quadRectMatrix
);
187 SkMatrix skDeviceMatrix
;
188 toSkMatrix(&skDeviceMatrix
, contentsDeviceTransform
);
189 m_skCurrentCanvas
->setMatrix(skDeviceMatrix
);
191 m_skCurrentPaint
.reset();
192 if (!isScaleAndTranslate(skDeviceMatrix
)) {
193 m_skCurrentPaint
.setAntiAlias(true);
194 m_skCurrentPaint
.setFilterBitmap(true);
197 if (quad
->ShouldDrawWithBlending()) {
198 m_skCurrentPaint
.setAlpha(quad
->opacity() * 255);
199 m_skCurrentPaint
.setXfermodeMode(SkXfermode::kSrcOver_Mode
);
201 m_skCurrentPaint
.setXfermodeMode(SkXfermode::kSrc_Mode
);
204 switch (quad
->material
) {
205 case DrawQuad::DEBUG_BORDER
:
206 drawDebugBorderQuad(frame
, DebugBorderDrawQuad::MaterialCast(quad
));
208 case DrawQuad::SOLID_COLOR
:
209 drawSolidColorQuad(frame
, SolidColorDrawQuad::MaterialCast(quad
));
211 case DrawQuad::TEXTURE_CONTENT
:
212 drawTextureQuad(frame
, TextureDrawQuad::MaterialCast(quad
));
214 case DrawQuad::TILED_CONTENT
:
215 drawTileQuad(frame
, TileDrawQuad::MaterialCast(quad
));
217 case DrawQuad::RENDER_PASS
:
218 drawRenderPassQuad(frame
, RenderPassDrawQuad::MaterialCast(quad
));
221 drawUnsupportedQuad(frame
, quad
);
225 m_skCurrentCanvas
->resetMatrix();
228 void SoftwareRenderer::drawDebugBorderQuad(const DrawingFrame
& frame
, const DebugBorderDrawQuad
* quad
)
230 // We need to apply the matrix manually to have pixel-sized stroke width.
232 gfx::RectFToSkRect(quadVertexRect()).toQuad(vertices
);
233 SkPoint transformedVertices
[4];
234 m_skCurrentCanvas
->getTotalMatrix().mapPoints(transformedVertices
, vertices
, 4);
235 m_skCurrentCanvas
->resetMatrix();
237 m_skCurrentPaint
.setColor(quad
->color
);
238 m_skCurrentPaint
.setAlpha(quad
->opacity() * SkColorGetA(quad
->color
));
239 m_skCurrentPaint
.setStyle(SkPaint::kStroke_Style
);
240 m_skCurrentPaint
.setStrokeWidth(quad
->width
);
241 m_skCurrentCanvas
->drawPoints(SkCanvas::kPolygon_PointMode
, 4, transformedVertices
, m_skCurrentPaint
);
244 void SoftwareRenderer::drawSolidColorQuad(const DrawingFrame
& frame
, const SolidColorDrawQuad
* quad
)
246 m_skCurrentPaint
.setColor(quad
->color
);
247 m_skCurrentPaint
.setAlpha(quad
->opacity() * SkColorGetA(quad
->color
));
248 m_skCurrentCanvas
->drawRect(gfx::RectFToSkRect(quadVertexRect()), m_skCurrentPaint
);
251 void SoftwareRenderer::drawTextureQuad(const DrawingFrame
& frame
, const TextureDrawQuad
* quad
)
253 if (!isSoftwareResource(quad
->resource_id
)) {
254 drawUnsupportedQuad(frame
, quad
);
258 // FIXME: Add support for non-premultiplied alpha.
259 ResourceProvider::ScopedReadLockSoftware
lock(m_resourceProvider
, quad
->resource_id
);
260 const SkBitmap
* bitmap
= lock
.skBitmap();
261 gfx::RectF uvRect
= gfx::ScaleRect(quad
->uv_rect
, bitmap
->width(), bitmap
->height());
262 SkRect skUvRect
= gfx::RectFToSkRect(uvRect
);
264 m_skCurrentCanvas
->scale(1, -1);
265 m_skCurrentCanvas
->drawBitmapRectToRect(*bitmap
, &skUvRect
,
266 gfx::RectFToSkRect(quadVertexRect()),
270 void SoftwareRenderer::drawTileQuad(const DrawingFrame
& frame
, const TileDrawQuad
* quad
)
272 DCHECK(isSoftwareResource(quad
->resource_id
));
273 ResourceProvider::ScopedReadLockSoftware
lock(m_resourceProvider
, quad
->resource_id
);
275 SkRect uvRect
= gfx::RectFToSkRect(quad
->tex_coord_rect
);
276 m_skCurrentPaint
.setFilterBitmap(true);
277 m_skCurrentCanvas
->drawBitmapRectToRect(*lock
.skBitmap(), &uvRect
,
278 gfx::RectFToSkRect(quadVertexRect()),
282 void SoftwareRenderer::drawRenderPassQuad(const DrawingFrame
& frame
, const RenderPassDrawQuad
* quad
)
284 CachedResource
* contentTexture
= m_renderPassTextures
.get(quad
->render_pass_id
);
285 if (!contentTexture
|| !contentTexture
->id())
288 DCHECK(isSoftwareResource(contentTexture
->id()));
289 ResourceProvider::ScopedReadLockSoftware
lock(m_resourceProvider
, contentTexture
->id());
291 SkRect destRect
= gfx::RectFToSkRect(quadVertexRect());
293 const SkBitmap
* content
= lock
.skBitmap();
296 content
->getBounds(&contentRect
);
299 contentMat
.setRectToRect(contentRect
, destRect
, SkMatrix::kFill_ScaleToFit
);
301 skia::RefPtr
<SkShader
> shader
= skia::AdoptRef(
302 SkShader::CreateBitmapShader(*content
,
303 SkShader::kClamp_TileMode
,
304 SkShader::kClamp_TileMode
));
305 shader
->setLocalMatrix(contentMat
);
306 m_skCurrentPaint
.setShader(shader
.get());
308 SkImageFilter
* filter
= quad
->filter
.get();
310 m_skCurrentPaint
.setImageFilter(filter
);
312 if (quad
->mask_resource_id
) {
313 ResourceProvider::ScopedReadLockSoftware
maskLock(m_resourceProvider
, quad
->mask_resource_id
);
315 const SkBitmap
* mask
= maskLock
.skBitmap();
317 SkRect maskRect
= SkRect::MakeXYWH(
318 quad
->mask_uv_rect
.x() * mask
->width(),
319 quad
->mask_uv_rect
.y() * mask
->height(),
320 quad
->mask_uv_rect
.width() * mask
->width(),
321 quad
->mask_uv_rect
.height() * mask
->height());
324 maskMat
.setRectToRect(maskRect
, destRect
, SkMatrix::kFill_ScaleToFit
);
326 skia::RefPtr
<SkShader
> maskShader
= skia::AdoptRef(
327 SkShader::CreateBitmapShader(*mask
,
328 SkShader::kClamp_TileMode
,
329 SkShader::kClamp_TileMode
));
330 maskShader
->setLocalMatrix(maskMat
);
333 maskPaint
.setShader(maskShader
.get());
335 skia::RefPtr
<SkLayerRasterizer
> maskRasterizer
= skia::AdoptRef(new SkLayerRasterizer
);
336 maskRasterizer
->addLayer(maskPaint
);
338 m_skCurrentPaint
.setRasterizer(maskRasterizer
.get());
339 m_skCurrentCanvas
->drawRect(destRect
, m_skCurrentPaint
);
341 // FIXME: Apply background filters and blend with content
342 m_skCurrentCanvas
->drawRect(destRect
, m_skCurrentPaint
);
346 void SoftwareRenderer::drawUnsupportedQuad(const DrawingFrame
& frame
, const DrawQuad
* quad
)
348 m_skCurrentPaint
.setColor(SK_ColorMAGENTA
);
349 m_skCurrentPaint
.setAlpha(quad
->opacity() * 255);
350 m_skCurrentCanvas
->drawRect(gfx::RectFToSkRect(quadVertexRect()), m_skCurrentPaint
);
353 bool SoftwareRenderer::swapBuffers()
355 if (m_client
->hasImplThread())
356 m_client
->onSwapBuffersComplete();
360 void SoftwareRenderer::getFramebufferPixels(void *pixels
, const gfx::Rect
& rect
)
362 TRACE_EVENT0("cc", "SoftwareRenderer::getFramebufferPixels");
363 SkBitmap fullBitmap
= m_outputDevice
->Lock(false)->getSkBitmap();
364 SkBitmap subsetBitmap
;
365 SkIRect invertRect
= SkIRect::MakeXYWH(rect
.x(), viewportSize().height() - rect
.bottom(), rect
.width(), rect
.height());
366 fullBitmap
.extractSubset(&subsetBitmap
, invertRect
);
367 subsetBitmap
.copyPixelsTo(pixels
, rect
.width() * rect
.height() * 4, rect
.width() * 4);
368 m_outputDevice
->Unlock();
371 void SoftwareRenderer::setVisible(bool visible
)
373 if (m_visible
== visible
)