OSDN Git Service

Merge WebKit at r78450: Initial merge by git.
[android-x86/external-webkit.git] / Source / WebCore / platform / graphics / chromium / LayerTilerChromium.cpp
1 /*
2  * Copyright (C) 2010 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
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.
13  *
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.
24  */
25
26
27 #include "config.h"
28
29 #if USE(ACCELERATED_COMPOSITING)
30
31 #include "LayerTilerChromium.h"
32
33 #include "GraphicsContext.h"
34 #include "GraphicsContext3D.h"
35 #include "LayerRendererChromium.h"
36 #include "LayerTexture.h"
37
38 #if PLATFORM(SKIA)
39 #include "NativeImageSkia.h"
40 #include "PlatformContextSkia.h"
41 #elif PLATFORM(CG)
42 #include <CoreGraphics/CGBitmapContext.h>
43 #endif
44
45 #include <wtf/PassOwnArrayPtr.h>
46
47 namespace WebCore {
48
49 PassOwnPtr<LayerTilerChromium> LayerTilerChromium::create(LayerRendererChromium* layerRenderer, const IntSize& tileSize)
50 {
51     if (!layerRenderer || tileSize.isEmpty())
52         return 0;
53
54     return adoptPtr(new LayerTilerChromium(layerRenderer, tileSize));
55 }
56
57 LayerTilerChromium::LayerTilerChromium(LayerRendererChromium* layerRenderer, const IntSize& tileSize)
58     : m_skipsDraw(false)
59     , m_layerRenderer(layerRenderer)
60 {
61     setTileSize(tileSize);
62 }
63
64 LayerTilerChromium::~LayerTilerChromium()
65 {
66     reset();
67 }
68
69 GraphicsContext3D* LayerTilerChromium::layerRendererContext() const
70 {
71     ASSERT(layerRenderer());
72     return layerRenderer()->context();
73 }
74
75 void LayerTilerChromium::setTileSize(const IntSize& size)
76 {
77     if (m_tileSize == size)
78         return;
79
80     reset();
81
82     m_tileSize = size;
83     m_tilePixels = adoptArrayPtr(new uint8_t[m_tileSize.width() * m_tileSize.height() * 4]);
84 }
85
86 void LayerTilerChromium::reset()
87 {
88     m_tiles.clear();
89     m_unusedTiles.clear();
90
91     m_layerSize = IntSize();
92     m_layerTileSize = IntSize();
93     m_lastUpdateLayerRect = IntRect();
94 }
95
96 LayerTilerChromium::Tile* LayerTilerChromium::createTile(int i, int j)
97 {
98     const int index = tileIndex(i, j);
99     ASSERT(!m_tiles[index]);
100
101     if (m_unusedTiles.size() > 0) {
102         m_tiles[index] = m_unusedTiles.last().release();
103         m_unusedTiles.removeLast();
104     } else {
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();
109     }
110
111     m_tiles[index]->m_dirtyLayerRect = tileLayerRect(i, j);
112     return m_tiles[index].get();
113 }
114
115 void LayerTilerChromium::invalidateTiles(const IntRect& oldLayerRect, const IntRect& newLayerRect)
116 {
117     if (!m_tiles.size())
118         return;
119
120     IntRect oldContentRect = layerRectToContentRect(oldLayerRect);
121     int oldLeft, oldTop, oldRight, oldBottom;
122     contentRectToTileIndices(oldContentRect, oldLeft, oldTop, oldRight, oldBottom);
123
124     IntRect newContentRect = layerRectToContentRect(newLayerRect);
125     int newLeft, newTop, newRight, newBottom;
126     contentRectToTileIndices(newContentRect, newLeft, newTop, newRight, newBottom);
127
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)
133                 continue;
134
135             const int index = tileIndex(i, j);
136             if (m_tiles[index])
137                 m_unusedTiles.append(m_tiles[index].release());
138         }
139     }
140 }
141
142 void LayerTilerChromium::contentRectToTileIndices(const IntRect& contentRect, int &left, int &top, int &right, int &bottom) const
143 {
144     const IntRect layerRect = contentRectToLayerRect(contentRect);
145
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();
150 }
151
152 IntRect LayerTilerChromium::contentRectToLayerRect(const IntRect& contentRect) const
153 {
154     IntPoint pos(contentRect.x() - m_layerPosition.x(), contentRect.y() - m_layerPosition.y());
155     IntRect layerRect(pos, contentRect.size());
156
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()));
160     return layerRect;
161 }
162
163 IntRect LayerTilerChromium::layerRectToContentRect(const IntRect& layerRect) const
164 {
165     IntRect contentRect = layerRect;
166     contentRect.move(m_layerPosition.x(), m_layerPosition.y());
167     return contentRect;
168 }
169
170 int LayerTilerChromium::tileIndex(int i, int j) const
171 {
172     ASSERT(i >= 0 && j >= 0 && i < m_layerTileSize.width() && j < m_layerTileSize.height());
173     return i + j * m_layerTileSize.width();
174 }
175
176 IntRect LayerTilerChromium::tileContentRect(int i, int j) const
177 {
178     IntPoint anchor(m_layerPosition.x() + i * m_tileSize.width(), m_layerPosition.y() + j * m_tileSize.height());
179     IntRect tile(anchor, m_tileSize);
180     return tile;
181 }
182
183 IntRect LayerTilerChromium::tileLayerRect(int i, int j) const
184 {
185     IntPoint anchor(i * m_tileSize.width(), j * m_tileSize.height());
186     IntRect tile(anchor, m_tileSize);
187     return tile;
188 }
189
190 void LayerTilerChromium::invalidateRect(const IntRect& contentRect)
191 {
192     if (contentRect.isEmpty())
193         return;
194
195     growLayerToContain(contentRect);
196
197     // Dirty rects are always in layer space, as the layer could be repositioned
198     // after invalidation.
199     IntRect layerRect = contentRectToLayerRect(contentRect);
200
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();
206             if (!tile)
207                 continue;
208             IntRect bound = tileLayerRect(i, j);
209             bound.intersect(layerRect);
210             tile->m_dirtyLayerRect.unite(bound);
211         }
212     }
213 }
214
215 void LayerTilerChromium::invalidateEntireLayer()
216 {
217     for (size_t i = 0; i < m_tiles.size(); ++i) {
218         if (m_tiles[i])
219             m_unusedTiles.append(m_tiles[i].release());
220     }
221     m_tiles.clear();
222
223     m_layerSize = IntSize();
224     m_layerTileSize = IntSize();
225     m_lastUpdateLayerRect = IntRect();
226 }
227
228 void LayerTilerChromium::update(TilePaintInterface& painter, const IntRect& contentRect)
229 {
230     if (m_skipsDraw)
231         return;
232
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;
238
239     growLayerToContain(contentRect);
240
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();
249             if (!tile)
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);
254         }
255     }
256
257     if (dirtyLayerRect.isEmpty())
258         return;
259
260     const IntRect paintRect = layerRectToContentRect(dirtyLayerRect);
261     GraphicsContext3D* context = layerRendererContext();
262 #if PLATFORM(SKIA)
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())));
266
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()));
269
270     painter.paint(*graphicsContext, paintRect);
271
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())
276         CRASH();
277     uint8_t* paintPixels = static_cast<uint8_t*>(bitmap.getPixels());
278     if (!paintPixels)
279         CRASH();
280 #elif PLATFORM(CG)
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,
289                                                        colorSpace.get(),
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()));
294
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);
298
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());
302 #else
303 #error "Need to implement for your platform."
304 #endif
305
306     // Painting could cause compositing to get turned off, which may cause the tiler to become invalidated mid-update.
307     if (!m_tiles.size())
308         return;
309
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();
313             if (!tile)
314                 CRASH();
315             if (!tile->dirty())
316                 continue;
317
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())
323                 continue;
324
325             if (!tile->texture()->reserve(m_tileSize, GraphicsContext3D::RGBA)) {
326                 m_skipsDraw = true;
327                 reset();
328                 return;
329             }
330
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)
334                 CRASH();
335             if (destRect.y() < 0)
336                 CRASH();
337
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)
341                 CRASH();
342             if (paintOffset.y() < 0)
343                 CRASH();
344             if (paintOffset.x() + destRect.width() > paintRect.width())
345                 CRASH();
346             if (paintOffset.y() + destRect.height() > paintRect.height())
347                 CRASH();
348
349             uint8_t* pixelSource;
350             if (paintRect.width() == sourceRect.width() && !paintOffset.x())
351                 pixelSource = &paintPixels[4 * paintOffset.y() * paintRect.width()];
352             else {
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);
359
360                 pixelSource = &m_tilePixels[0];
361             }
362
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));
365
366             tile->clearDirty();
367         }
368     }
369 }
370
371 void LayerTilerChromium::setLayerPosition(const IntPoint& layerPosition)
372 {
373     m_layerPosition = layerPosition;
374 }
375
376 void LayerTilerChromium::draw(const IntRect& contentRect)
377 {
378     if (m_skipsDraw || !m_tiles.size())
379         return;
380
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));
386
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();
392             ASSERT(tile);
393
394             tile->texture()->bindTexture();
395
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);
399
400             LayerChromium::drawTexturedQuad(context, layerRenderer()->projectionMatrix(), tileMatrix, m_tileSize.width(), m_tileSize.height(), 1, contentLayerValues->shaderMatrixLocation(), contentLayerValues->shaderAlphaLocation());
401
402             tile->texture()->unreserve();
403         }
404     }
405 }
406
407 void LayerTilerChromium::resizeLayer(const IntSize& size)
408 {
409     if (m_layerSize == size)
410         return;
411
412     int width = (size.width() + m_tileSize.width() - 1) / m_tileSize.width();
413     int height = (size.height() + m_tileSize.height() - 1) / m_tileSize.height();
414
415     if (height && (width > INT_MAX / height))
416         CRASH();
417
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()]);
423
424     m_tiles.swap(newTiles);
425     m_layerSize = size;
426     m_layerTileSize = IntSize(width, height);
427 }
428
429 void LayerTilerChromium::growLayerToContain(const IntRect& contentRect)
430 {
431     // Grow the tile array to contain this content rect.
432     IntRect layerRect = contentRectToLayerRect(contentRect);
433     IntSize layerSize = IntSize(layerRect.maxX(), layerRect.maxY());
434
435     IntSize newSize = layerSize.expandedTo(m_layerSize);
436     resizeLayer(newSize);
437 }
438
439 } // namespace WebCore
440
441 #endif // USE(ACCELERATED_COMPOSITING)