2 * Copyright (C) 2010 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #if USE(ACCELERATED_COMPOSITING)
31 #include "LayerTilerChromium.h"
33 #include "GraphicsContext.h"
34 #include "GraphicsContext3D.h"
35 #include "LayerRendererChromium.h"
36 #include "LayerTexture.h"
39 #include "NativeImageSkia.h"
40 #include "PlatformContextSkia.h"
42 #include <CoreGraphics/CGBitmapContext.h>
45 #include <wtf/PassOwnArrayPtr.h>
49 PassOwnPtr<LayerTilerChromium> LayerTilerChromium::create(LayerRendererChromium* layerRenderer, const IntSize& tileSize)
51 if (!layerRenderer || tileSize.isEmpty())
54 return adoptPtr(new LayerTilerChromium(layerRenderer, tileSize));
57 LayerTilerChromium::LayerTilerChromium(LayerRendererChromium* layerRenderer, const IntSize& tileSize)
59 , m_layerRenderer(layerRenderer)
61 setTileSize(tileSize);
64 LayerTilerChromium::~LayerTilerChromium()
69 GraphicsContext3D* LayerTilerChromium::layerRendererContext() const
71 ASSERT(layerRenderer());
72 return layerRenderer()->context();
75 void LayerTilerChromium::setTileSize(const IntSize& size)
77 if (m_tileSize == size)
83 m_tilePixels = adoptArrayPtr(new uint8_t[m_tileSize.width() * m_tileSize.height() * 4]);
86 void LayerTilerChromium::reset()
89 m_unusedTiles.clear();
91 m_layerSize = IntSize();
92 m_layerTileSize = IntSize();
93 m_lastUpdateLayerRect = IntRect();
96 LayerTilerChromium::Tile* LayerTilerChromium::createTile(int i, int j)
98 const int index = tileIndex(i, j);
99 ASSERT(!m_tiles[index]);
101 if (m_unusedTiles.size() > 0) {
102 m_tiles[index] = m_unusedTiles.last().release();
103 m_unusedTiles.removeLast();
105 GraphicsContext3D* context = layerRendererContext();
106 TextureManager* manager = layerRenderer()->textureManager();
107 OwnPtr<Tile> tile = adoptPtr(new Tile(LayerTexture::create(context, manager)));
108 m_tiles[index] = tile.release();
111 m_tiles[index]->m_dirtyLayerRect = tileLayerRect(i, j);
112 return m_tiles[index].get();
115 void LayerTilerChromium::invalidateTiles(const IntRect& oldLayerRect, const IntRect& newLayerRect)
120 IntRect oldContentRect = layerRectToContentRect(oldLayerRect);
121 int oldLeft, oldTop, oldRight, oldBottom;
122 contentRectToTileIndices(oldContentRect, oldLeft, oldTop, oldRight, oldBottom);
124 IntRect newContentRect = layerRectToContentRect(newLayerRect);
125 int newLeft, newTop, newRight, newBottom;
126 contentRectToTileIndices(newContentRect, newLeft, newTop, newRight, newBottom);
128 // Iterating through just the old tile indices is an optimization to avoid
129 // iterating through the entire m_tiles array.
130 for (int j = oldTop; j <= oldBottom; ++j) {
131 for (int i = oldLeft; i <= oldRight; ++i) {
132 if (i >= newLeft && i <= newRight && j >= newTop && j <= newBottom)
135 const int index = tileIndex(i, j);
137 m_unusedTiles.append(m_tiles[index].release());
142 void LayerTilerChromium::contentRectToTileIndices(const IntRect& contentRect, int &left, int &top, int &right, int &bottom) const
144 const IntRect layerRect = contentRectToLayerRect(contentRect);
146 left = layerRect.x() / m_tileSize.width();
147 top = layerRect.y() / m_tileSize.height();
148 right = (layerRect.maxX() - 1) / m_tileSize.width();
149 bottom = (layerRect.maxY() - 1) / m_tileSize.height();
152 IntRect LayerTilerChromium::contentRectToLayerRect(const IntRect& contentRect) const
154 IntPoint pos(contentRect.x() - m_layerPosition.x(), contentRect.y() - m_layerPosition.y());
155 IntRect layerRect(pos, contentRect.size());
157 // Clip to the position.
158 if (pos.x() < 0 || pos.y() < 0)
159 layerRect = IntRect(IntPoint(0, 0), IntSize(contentRect.width() + pos.x(), contentRect.height() + pos.y()));
163 IntRect LayerTilerChromium::layerRectToContentRect(const IntRect& layerRect) const
165 IntRect contentRect = layerRect;
166 contentRect.move(m_layerPosition.x(), m_layerPosition.y());
170 int LayerTilerChromium::tileIndex(int i, int j) const
172 ASSERT(i >= 0 && j >= 0 && i < m_layerTileSize.width() && j < m_layerTileSize.height());
173 return i + j * m_layerTileSize.width();
176 IntRect LayerTilerChromium::tileContentRect(int i, int j) const
178 IntPoint anchor(m_layerPosition.x() + i * m_tileSize.width(), m_layerPosition.y() + j * m_tileSize.height());
179 IntRect tile(anchor, m_tileSize);
183 IntRect LayerTilerChromium::tileLayerRect(int i, int j) const
185 IntPoint anchor(i * m_tileSize.width(), j * m_tileSize.height());
186 IntRect tile(anchor, m_tileSize);
190 void LayerTilerChromium::invalidateRect(const IntRect& contentRect)
192 if (contentRect.isEmpty())
195 growLayerToContain(contentRect);
197 // Dirty rects are always in layer space, as the layer could be repositioned
198 // after invalidation.
199 IntRect layerRect = contentRectToLayerRect(contentRect);
201 int left, top, right, bottom;
202 contentRectToTileIndices(contentRect, left, top, right, bottom);
203 for (int j = top; j <= bottom; ++j) {
204 for (int i = left; i <= right; ++i) {
205 Tile* tile = m_tiles[tileIndex(i, j)].get();
208 IntRect bound = tileLayerRect(i, j);
209 bound.intersect(layerRect);
210 tile->m_dirtyLayerRect.unite(bound);
215 void LayerTilerChromium::invalidateEntireLayer()
217 for (size_t i = 0; i < m_tiles.size(); ++i) {
219 m_unusedTiles.append(m_tiles[i].release());
223 m_layerSize = IntSize();
224 m_layerTileSize = IntSize();
225 m_lastUpdateLayerRect = IntRect();
228 void LayerTilerChromium::update(TilePaintInterface& painter, const IntRect& contentRect)
233 // Invalidate old tiles that were previously used but aren't in use this
234 // frame so that they can get reused for new tiles.
235 IntRect layerRect = contentRectToLayerRect(contentRect);
236 invalidateTiles(m_lastUpdateLayerRect, layerRect);
237 m_lastUpdateLayerRect = layerRect;
239 growLayerToContain(contentRect);
241 // Create tiles as needed, expanding a dirty rect to contain all
242 // the dirty regions currently being drawn.
243 IntRect dirtyLayerRect;
244 int left, top, right, bottom;
245 contentRectToTileIndices(contentRect, left, top, right, bottom);
246 for (int j = top; j <= bottom; ++j) {
247 for (int i = left; i <= right; ++i) {
248 Tile* tile = m_tiles[tileIndex(i, j)].get();
250 tile = createTile(i, j);
251 if (!tile->texture()->isValid(m_tileSize, GraphicsContext3D::RGBA))
252 tile->m_dirtyLayerRect = tileLayerRect(i, j);
253 dirtyLayerRect.unite(tile->m_dirtyLayerRect);
257 if (dirtyLayerRect.isEmpty())
260 const IntRect paintRect = layerRectToContentRect(dirtyLayerRect);
261 GraphicsContext3D* context = layerRendererContext();
263 OwnPtr<skia::PlatformCanvas> canvas(new skia::PlatformCanvas(paintRect.width(), paintRect.height(), false));
264 OwnPtr<PlatformContextSkia> skiaContext(new PlatformContextSkia(canvas.get()));
265 OwnPtr<GraphicsContext> graphicsContext(new GraphicsContext(reinterpret_cast<PlatformGraphicsContext*>(skiaContext.get())));
267 // Bring the canvas into the coordinate system of the paint rect.
268 canvas->translate(static_cast<SkScalar>(-paintRect.x()), static_cast<SkScalar>(-paintRect.y()));
270 painter.paint(*graphicsContext, paintRect);
272 // Get the contents of the updated rect.
273 const SkBitmap& bitmap = canvas->getDevice()->accessBitmap(false);
274 ASSERT(bitmap.width() == paintRect.width() && bitmap.height() == paintRect.height());
275 if (bitmap.width() != paintRect.width() || bitmap.height() != paintRect.height())
277 uint8_t* paintPixels = static_cast<uint8_t*>(bitmap.getPixels());
281 Vector<uint8_t> canvasPixels;
282 int rowBytes = 4 * paintRect.width();
283 canvasPixels.resize(rowBytes * paintRect.height());
284 memset(canvasPixels.data(), 0, canvasPixels.size());
285 RetainPtr<CGColorSpaceRef> colorSpace(AdoptCF, CGColorSpaceCreateDeviceRGB());
286 RetainPtr<CGContextRef> m_cgContext;
287 m_cgContext.adoptCF(CGBitmapContextCreate(canvasPixels.data(),
288 paintRect.width(), paintRect.height(), 8, rowBytes,
290 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host));
291 CGContextTranslateCTM(m_cgContext.get(), 0, paintRect.height());
292 CGContextScaleCTM(m_cgContext.get(), 1, -1);
293 OwnPtr<GraphicsContext> m_graphicsContext(new GraphicsContext(m_cgContext.get()));
295 // Bring the CoreGraphics context into the coordinate system of the paint rect.
296 CGContextTranslateCTM(m_cgContext.get(), -paintRect.x(), -paintRect.y());
297 painter.paint(*m_graphicsContext, paintRect);
299 // Get the contents of the updated rect.
300 ASSERT(static_cast<int>(CGBitmapContextGetWidth(m_cgContext.get())) == paintRect.width() && static_cast<int>(CGBitmapContextGetHeight(m_cgContext.get())) == paintRect.height());
301 uint8_t* paintPixels = static_cast<uint8_t*>(canvasPixels.data());
303 #error "Need to implement for your platform."
306 // Painting could cause compositing to get turned off, which may cause the tiler to become invalidated mid-update.
310 for (int j = top; j <= bottom; ++j) {
311 for (int i = left; i <= right; ++i) {
312 Tile* tile = m_tiles[tileIndex(i, j)].get();
318 // Calculate page-space rectangle to copy from.
319 IntRect sourceRect = tileContentRect(i, j);
320 const IntPoint anchor = sourceRect.location();
321 sourceRect.intersect(layerRectToContentRect(tile->m_dirtyLayerRect));
322 if (sourceRect.isEmpty())
325 if (!tile->texture()->reserve(m_tileSize, GraphicsContext3D::RGBA)) {
331 // Calculate tile-space rectangle to upload into.
332 IntRect destRect(IntPoint(sourceRect.x() - anchor.x(), sourceRect.y() - anchor.y()), sourceRect.size());
333 if (destRect.x() < 0)
335 if (destRect.y() < 0)
338 // Offset from paint rectangle to this tile's dirty rectangle.
339 IntPoint paintOffset(sourceRect.x() - paintRect.x(), sourceRect.y() - paintRect.y());
340 if (paintOffset.x() < 0)
342 if (paintOffset.y() < 0)
344 if (paintOffset.x() + destRect.width() > paintRect.width())
346 if (paintOffset.y() + destRect.height() > paintRect.height())
349 uint8_t* pixelSource;
350 if (paintRect.width() == sourceRect.width() && !paintOffset.x())
351 pixelSource = &paintPixels[4 * paintOffset.y() * paintRect.width()];
353 // Strides not equal, so do a row-by-row memcpy from the
354 // paint results into a temp buffer for uploading.
355 for (int row = 0; row < destRect.height(); ++row)
356 memcpy(&m_tilePixels[destRect.width() * 4 * row],
357 &paintPixels[4 * (paintOffset.x() + (paintOffset.y() + row) * paintRect.width())],
358 destRect.width() * 4);
360 pixelSource = &m_tilePixels[0];
363 tile->texture()->bindTexture();
364 GLC(context, context->texSubImage2D(GraphicsContext3D::TEXTURE_2D, 0, destRect.x(), destRect.y(), destRect.width(), destRect.height(), GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, pixelSource));
371 void LayerTilerChromium::setLayerPosition(const IntPoint& layerPosition)
373 m_layerPosition = layerPosition;
376 void LayerTilerChromium::draw(const IntRect& contentRect)
378 if (m_skipsDraw || !m_tiles.size())
381 // We reuse the shader program used by ContentLayerChromium.
382 GraphicsContext3D* context = layerRendererContext();
383 const ContentLayerChromium::SharedValues* contentLayerValues = layerRenderer()->contentLayerSharedValues();
384 layerRenderer()->useShader(contentLayerValues->contentShaderProgram());
385 GLC(context, context->uniform1i(contentLayerValues->shaderSamplerLocation(), 0));
387 int left, top, right, bottom;
388 contentRectToTileIndices(contentRect, left, top, right, bottom);
389 for (int j = top; j <= bottom; ++j) {
390 for (int i = left; i <= right; ++i) {
391 Tile* tile = m_tiles[tileIndex(i, j)].get();
394 tile->texture()->bindTexture();
396 TransformationMatrix tileMatrix;
397 IntRect tileRect = tileContentRect(i, j);
398 tileMatrix.translate3d(tileRect.x() - contentRect.x() + tileRect.width() / 2.0, tileRect.y() - contentRect.y() + tileRect.height() / 2.0, 0);
400 LayerChromium::drawTexturedQuad(context, layerRenderer()->projectionMatrix(), tileMatrix, m_tileSize.width(), m_tileSize.height(), 1, contentLayerValues->shaderMatrixLocation(), contentLayerValues->shaderAlphaLocation());
402 tile->texture()->unreserve();
407 void LayerTilerChromium::resizeLayer(const IntSize& size)
409 if (m_layerSize == size)
412 int width = (size.width() + m_tileSize.width() - 1) / m_tileSize.width();
413 int height = (size.height() + m_tileSize.height() - 1) / m_tileSize.height();
415 if (height && (width > INT_MAX / height))
418 Vector<OwnPtr<Tile> > newTiles;
419 newTiles.resize(width * height);
420 for (int j = 0; j < m_layerTileSize.height(); ++j)
421 for (int i = 0; i < m_layerTileSize.width(); ++i)
422 newTiles[i + j * width].swap(m_tiles[i + j * m_layerTileSize.width()]);
424 m_tiles.swap(newTiles);
426 m_layerTileSize = IntSize(width, height);
429 void LayerTilerChromium::growLayerToContain(const IntRect& contentRect)
431 // Grow the tile array to contain this content rect.
432 IntRect layerRect = contentRectToLayerRect(contentRect);
433 IntSize layerSize = IntSize(layerRect.maxX(), layerRect.maxY());
435 IntSize newSize = layerSize.expandedTo(m_layerSize);
436 resizeLayer(newSize);
439 } // namespace WebCore
441 #endif // USE(ACCELERATED_COMPOSITING)