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/SkDevice.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_isScissorEnabled(false)
64 , m_outputDevice(outputDevice
)
65 , m_skCurrentCanvas(0)
67 m_resourceProvider
->setDefaultResourceType(ResourceProvider::Bitmap
);
69 m_capabilities
.maxTextureSize
= m_resourceProvider
->maxTextureSize();
70 m_capabilities
.bestTextureFormat
= m_resourceProvider
->bestTextureFormat();
71 m_capabilities
.usingSetVisibility
= true;
72 // The updater can access bitmaps while the SoftwareRenderer is using them.
73 m_capabilities
.allowPartialTextureUpdates
= true;
74 m_capabilities
.usingPartialSwap
= true;
79 SoftwareRenderer::~SoftwareRenderer()
83 const RendererCapabilities
& SoftwareRenderer::capabilities() const
85 return m_capabilities
;
88 void SoftwareRenderer::viewportChanged()
90 m_outputDevice
->DidChangeViewportSize(viewportSize());
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::ensureScissorTestEnabled()
115 m_isScissorEnabled
= true;
116 setClipRect(m_scissorRect
);
119 void SoftwareRenderer::ensureScissorTestDisabled()
121 // There is no explicit notion of enabling/disabling scissoring in software
122 // rendering, but the underlying effect we want is to clear any existing
123 // clipRect on the current SkCanvas. This is done by setting clipRect to
124 // the viewport's dimensions.
125 m_isScissorEnabled
= false;
126 SkDevice
* device
= m_skCurrentCanvas
->getDevice();
127 setClipRect(gfx::Rect(device
->width(), device
->height()));
130 void SoftwareRenderer::finish()
134 void SoftwareRenderer::bindFramebufferToOutputSurface(DrawingFrame
& frame
)
136 m_currentFramebufferLock
.reset();
137 m_skCurrentCanvas
= m_skRootCanvas
.get();
140 bool SoftwareRenderer::bindFramebufferToTexture(DrawingFrame
& frame
, const ScopedResource
* texture
, const gfx::Rect
& framebufferRect
)
142 m_currentFramebufferLock
= make_scoped_ptr(new ResourceProvider::ScopedWriteLockSoftware(m_resourceProvider
, texture
->id()));
143 m_skCurrentCanvas
= m_currentFramebufferLock
->skCanvas();
144 initializeMatrices(frame
, framebufferRect
, false);
145 setDrawViewportSize(framebufferRect
.size());
150 void SoftwareRenderer::setScissorTestRect(const gfx::Rect
& scissorRect
)
152 m_isScissorEnabled
= true;
153 m_scissorRect
= scissorRect
;
154 setClipRect(scissorRect
);
157 void SoftwareRenderer::setClipRect(const gfx::Rect
& rect
)
159 // Skia applies the current matrix to clip rects so we reset it temporary.
160 SkMatrix currentMatrix
= m_skCurrentCanvas
->getTotalMatrix();
161 m_skCurrentCanvas
->resetMatrix();
162 m_skCurrentCanvas
->clipRect(gfx::RectToSkRect(rect
), SkRegion::kReplace_Op
);
163 m_skCurrentCanvas
->setMatrix(currentMatrix
);
166 void SoftwareRenderer::clearCanvas(SkColor color
)
168 // SkCanvas::clear doesn't respect the current clipping region
169 // so we SkCanvas::drawColor instead if scissoring is active.
170 if (m_isScissorEnabled
)
171 m_skCurrentCanvas
->drawColor(color
, SkXfermode::kSrc_Mode
);
173 m_skCurrentCanvas
->clear(color
);
176 void SoftwareRenderer::clearFramebuffer(DrawingFrame
& frame
)
178 if (frame
.currentRenderPass
->has_transparent_background
) {
179 clearCanvas(SkColorSetARGB(0, 0, 0, 0));
182 // On DEBUG builds, opaque render passes are cleared to blue to easily see regions that were not drawn on the screen.
183 clearCanvas(SkColorSetARGB(255, 0, 0, 255));
188 void SoftwareRenderer::setDrawViewportSize(const gfx::Size
& viewportSize
)
192 bool SoftwareRenderer::isSoftwareResource(ResourceProvider::ResourceId id
) const
194 switch (m_resourceProvider
->resourceType(id
)) {
195 case ResourceProvider::GLTexture
:
197 case ResourceProvider::Bitmap
:
201 LOG(FATAL
) << "Invalid resource type.";
205 void SoftwareRenderer::drawQuad(DrawingFrame
& frame
, const DrawQuad
* quad
)
207 TRACE_EVENT0("cc", "SoftwareRenderer::drawQuad");
208 gfx::Transform quadRectMatrix
;
209 quadRectTransform(&quadRectMatrix
, quad
->quadTransform(), quad
->rect
);
210 gfx::Transform contentsDeviceTransform
= frame
.windowMatrix
* frame
.projectionMatrix
* quadRectMatrix
;
211 contentsDeviceTransform
.FlattenTo2d();
212 SkMatrix skDeviceMatrix
;
213 toSkMatrix(&skDeviceMatrix
, contentsDeviceTransform
);
214 m_skCurrentCanvas
->setMatrix(skDeviceMatrix
);
216 m_skCurrentPaint
.reset();
217 if (!isScaleAndTranslate(skDeviceMatrix
)) {
218 m_skCurrentPaint
.setAntiAlias(true);
219 m_skCurrentPaint
.setFilterBitmap(true);
222 if (quad
->ShouldDrawWithBlending()) {
223 m_skCurrentPaint
.setAlpha(quad
->opacity() * 255);
224 m_skCurrentPaint
.setXfermodeMode(SkXfermode::kSrcOver_Mode
);
226 m_skCurrentPaint
.setXfermodeMode(SkXfermode::kSrc_Mode
);
229 switch (quad
->material
) {
230 case DrawQuad::DEBUG_BORDER
:
231 drawDebugBorderQuad(frame
, DebugBorderDrawQuad::MaterialCast(quad
));
233 case DrawQuad::SOLID_COLOR
:
234 drawSolidColorQuad(frame
, SolidColorDrawQuad::MaterialCast(quad
));
236 case DrawQuad::TEXTURE_CONTENT
:
237 drawTextureQuad(frame
, TextureDrawQuad::MaterialCast(quad
));
239 case DrawQuad::TILED_CONTENT
:
240 drawTileQuad(frame
, TileDrawQuad::MaterialCast(quad
));
242 case DrawQuad::RENDER_PASS
:
243 drawRenderPassQuad(frame
, RenderPassDrawQuad::MaterialCast(quad
));
246 drawUnsupportedQuad(frame
, quad
);
250 m_skCurrentCanvas
->resetMatrix();
253 void SoftwareRenderer::drawDebugBorderQuad(const DrawingFrame
& frame
, const DebugBorderDrawQuad
* quad
)
255 // We need to apply the matrix manually to have pixel-sized stroke width.
257 gfx::RectFToSkRect(quadVertexRect()).toQuad(vertices
);
258 SkPoint transformedVertices
[4];
259 m_skCurrentCanvas
->getTotalMatrix().mapPoints(transformedVertices
, vertices
, 4);
260 m_skCurrentCanvas
->resetMatrix();
262 m_skCurrentPaint
.setColor(quad
->color
);
263 m_skCurrentPaint
.setAlpha(quad
->opacity() * SkColorGetA(quad
->color
));
264 m_skCurrentPaint
.setStyle(SkPaint::kStroke_Style
);
265 m_skCurrentPaint
.setStrokeWidth(quad
->width
);
266 m_skCurrentCanvas
->drawPoints(SkCanvas::kPolygon_PointMode
, 4, transformedVertices
, m_skCurrentPaint
);
269 void SoftwareRenderer::drawSolidColorQuad(const DrawingFrame
& frame
, const SolidColorDrawQuad
* quad
)
271 m_skCurrentPaint
.setColor(quad
->color
);
272 m_skCurrentPaint
.setAlpha(quad
->opacity() * SkColorGetA(quad
->color
));
273 m_skCurrentCanvas
->drawRect(gfx::RectFToSkRect(quadVertexRect()), m_skCurrentPaint
);
276 void SoftwareRenderer::drawTextureQuad(const DrawingFrame
& frame
, const TextureDrawQuad
* quad
)
278 if (!isSoftwareResource(quad
->resource_id
)) {
279 drawUnsupportedQuad(frame
, quad
);
283 // FIXME: Add support for non-premultiplied alpha.
284 ResourceProvider::ScopedReadLockSoftware
lock(m_resourceProvider
, quad
->resource_id
);
285 const SkBitmap
* bitmap
= lock
.skBitmap();
286 gfx::RectF uvRect
= gfx::ScaleRect(gfx::BoundingRect(quad
->uv_top_left
, quad
->uv_bottom_right
),
289 SkRect skUvRect
= gfx::RectFToSkRect(uvRect
);
291 m_skCurrentCanvas
->scale(1, -1);
292 m_skCurrentCanvas
->drawBitmapRectToRect(*bitmap
, &skUvRect
,
293 gfx::RectFToSkRect(quadVertexRect()),
297 void SoftwareRenderer::drawTileQuad(const DrawingFrame
& frame
, const TileDrawQuad
* quad
)
299 DCHECK(isSoftwareResource(quad
->resource_id
));
300 ResourceProvider::ScopedReadLockSoftware
lock(m_resourceProvider
, quad
->resource_id
);
302 SkRect uvRect
= gfx::RectFToSkRect(quad
->tex_coord_rect
);
303 m_skCurrentPaint
.setFilterBitmap(true);
304 m_skCurrentCanvas
->drawBitmapRectToRect(*lock
.skBitmap(), &uvRect
,
305 gfx::RectFToSkRect(quadVertexRect()),
309 void SoftwareRenderer::drawRenderPassQuad(const DrawingFrame
& frame
, const RenderPassDrawQuad
* quad
)
311 CachedResource
* contentTexture
= m_renderPassTextures
.get(quad
->render_pass_id
);
312 if (!contentTexture
|| !contentTexture
->id())
315 DCHECK(isSoftwareResource(contentTexture
->id()));
316 ResourceProvider::ScopedReadLockSoftware
lock(m_resourceProvider
, contentTexture
->id());
318 SkRect destRect
= gfx::RectFToSkRect(quadVertexRect());
319 SkRect contentRect
= SkRect::MakeWH(quad
->rect
.width(), quad
->rect
.height());
322 contentMat
.setRectToRect(contentRect
, destRect
, SkMatrix::kFill_ScaleToFit
);
324 const SkBitmap
* content
= lock
.skBitmap();
325 skia::RefPtr
<SkShader
> shader
= skia::AdoptRef(
326 SkShader::CreateBitmapShader(*content
,
327 SkShader::kClamp_TileMode
,
328 SkShader::kClamp_TileMode
));
329 shader
->setLocalMatrix(contentMat
);
330 m_skCurrentPaint
.setShader(shader
.get());
332 SkImageFilter
* filter
= quad
->filter
.get();
334 m_skCurrentPaint
.setImageFilter(filter
);
336 if (quad
->mask_resource_id
) {
337 ResourceProvider::ScopedReadLockSoftware
maskLock(m_resourceProvider
, quad
->mask_resource_id
);
339 const SkBitmap
* mask
= maskLock
.skBitmap();
341 SkRect maskRect
= SkRect::MakeXYWH(
342 quad
->mask_uv_rect
.x() * mask
->width(),
343 quad
->mask_uv_rect
.y() * mask
->height(),
344 quad
->mask_uv_rect
.width() * mask
->width(),
345 quad
->mask_uv_rect
.height() * mask
->height());
348 maskMat
.setRectToRect(maskRect
, destRect
, SkMatrix::kFill_ScaleToFit
);
350 skia::RefPtr
<SkShader
> maskShader
= skia::AdoptRef(
351 SkShader::CreateBitmapShader(*mask
,
352 SkShader::kClamp_TileMode
,
353 SkShader::kClamp_TileMode
));
354 maskShader
->setLocalMatrix(maskMat
);
357 maskPaint
.setShader(maskShader
.get());
359 skia::RefPtr
<SkLayerRasterizer
> maskRasterizer
= skia::AdoptRef(new SkLayerRasterizer
);
360 maskRasterizer
->addLayer(maskPaint
);
362 m_skCurrentPaint
.setRasterizer(maskRasterizer
.get());
363 m_skCurrentCanvas
->drawRect(destRect
, m_skCurrentPaint
);
365 // FIXME: Apply background filters and blend with content
366 m_skCurrentCanvas
->drawRect(destRect
, m_skCurrentPaint
);
370 void SoftwareRenderer::drawUnsupportedQuad(const DrawingFrame
& frame
, const DrawQuad
* quad
)
372 m_skCurrentPaint
.setColor(SK_ColorMAGENTA
);
373 m_skCurrentPaint
.setAlpha(quad
->opacity() * 255);
374 m_skCurrentCanvas
->drawRect(gfx::RectFToSkRect(quadVertexRect()), m_skCurrentPaint
);
377 bool SoftwareRenderer::swapBuffers()
379 if (m_client
->hasImplThread())
380 m_client
->onSwapBuffersComplete();
384 void SoftwareRenderer::getFramebufferPixels(void *pixels
, const gfx::Rect
& rect
)
386 TRACE_EVENT0("cc", "SoftwareRenderer::getFramebufferPixels");
387 SkBitmap fullBitmap
= m_outputDevice
->Lock(false)->getSkBitmap();
388 SkBitmap subsetBitmap
;
389 SkIRect invertRect
= SkIRect::MakeXYWH(rect
.x(), viewportSize().height() - rect
.bottom(), rect
.width(), rect
.height());
390 fullBitmap
.extractSubset(&subsetBitmap
, invertRect
);
391 subsetBitmap
.copyPixelsTo(pixels
, rect
.width() * rect
.height() * 4, rect
.width() * 4);
392 m_outputDevice
->Unlock();
395 void SoftwareRenderer::setVisible(bool visible
)
397 if (m_visible
== visible
)