eb915dfc2c58c182ce69dffcd087be61f04d2592
[WebKit-https.git] / Source / WebCore / platform / graphics / ca / win / PlatformCALayerWinInternal.cpp
1 /*
2  * Copyright (C) 2011 Apple 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  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27
28 #include "PlatformCALayerWinInternal.h"
29
30 #include "Font.h"
31 #include "FontCache.h"
32 #include "GraphicsContext.h"
33 #include "PlatformCALayer.h"
34 #include "TextRun.h"
35 #include "TileController.h"
36 #include "TiledBacking.h"
37 #include <QuartzCore/CACFLayer.h>
38 #include <wtf/MainThread.h>
39
40 using namespace std;
41 using namespace WebCore;
42
43 // The width and height of a single tile in a tiled layer. Should be large enough to
44 // avoid lots of small tiles (and therefore lots of drawing callbacks), but small enough
45 // to keep the overall tile cost low.
46 static const int cTiledLayerTileSize = 512;
47
48 static bool layerTypeIsTiled(const PlatformCALayer::LayerType layerType)
49 {
50     return layerType == PlatformCALayer::LayerTypeWebTiledLayer
51         || layerType == PlatformCALayer::LayerTypePageTiledBackingLayer
52         || layerType == PlatformCALayer::LayerTypeTiledBackingLayer;
53 }
54
55 PlatformCALayerWinInternal::PlatformCALayerWinInternal(PlatformCALayer* owner)
56     : m_tileSize(CGSizeMake(cTiledLayerTileSize, cTiledLayerTileSize))
57     , m_constrainedSize(constrainedSize(owner->bounds().size()))
58     , m_owner(owner)
59 {
60     if (layerTypeIsTiled(m_owner->layerType())) {
61         // Tiled layers are placed in a child layer that is always the first child of the TiledLayer
62         m_tileParent = adoptCF(CACFLayerCreate(kCACFLayer));
63         CACFLayerInsertSublayer(m_owner->platformLayer(), m_tileParent.get(), 0);
64         updateTiles();
65     }
66 }
67
68 PlatformCALayerWinInternal::~PlatformCALayerWinInternal()
69 {
70 }
71
72 void PlatformCALayerWinInternal::displayCallback(CACFLayerRef caLayer, CGContextRef context)
73 {
74     ASSERT(isMainThread());
75     
76     if (!owner() || !owner()->owner())
77         return;
78
79     CGContextSaveGState(context);
80
81     CGRect layerBounds = owner()->bounds();
82     if (owner()->owner()->platformCALayerContentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesTopDown) {
83         CGContextScaleCTM(context, 1, -1);
84         CGContextTranslateCTM(context, 0, -layerBounds.size.height);
85     }
86
87     if (owner()->owner()) {
88         GraphicsContext graphicsContext(context);
89
90         // It's important to get the clip from the context, because it may be significantly
91         // smaller than the layer bounds (e.g. tiled layers)
92         CGRect clipBounds = CGContextGetClipBoundingBox(context);
93         IntRect clip(enclosingIntRect(clipBounds));
94         owner()->owner()->platformCALayerPaintContents(owner(), graphicsContext, clip);
95     }
96 #ifndef NDEBUG
97     else {
98         ASSERT_NOT_REACHED();
99
100         // FIXME: ideally we'd avoid calling -setNeedsDisplay on a layer that is a plain color,
101         // so CA never makes backing store for it (which is what -setNeedsDisplay will do above).
102         CGContextSetRGBFillColor(context, 0.0f, 1.0f, 0.0f, 1.0f);
103         CGContextFillRect(context, layerBounds);
104     }
105 #endif
106
107     if (owner()->owner()->platformCALayerShowRepaintCounter(owner())) {
108         FontCachePurgePreventer fontCachePurgePreventer;
109
110         String text = String::number(owner()->owner()->platformCALayerIncrementRepaintCount(owner()));
111
112         CGContextSaveGState(context);
113
114         // Make the background of the counter the same as the border color,
115         // unless there is no border, then make it red
116         float borderWidth = CACFLayerGetBorderWidth(caLayer);
117         if (borderWidth > 0) {
118             CGColorRef borderColor = CACFLayerGetBorderColor(caLayer);
119             const CGFloat* colors = CGColorGetComponents(borderColor);
120             CGContextSetRGBFillColor(context, colors[0], colors[1], colors[2], colors[3]);
121         } else
122             CGContextSetRGBFillColor(context, 1.0f, 0.0f, 0.0f, 0.8f);
123         
124         CGRect aBounds = layerBounds;
125
126         aBounds.size.width = 10 + 10 * text.length();
127         aBounds.size.height = 22;
128         CGContextFillRect(context, aBounds);
129         
130         FontDescription desc;
131
132         NONCLIENTMETRICS metrics;
133         metrics.cbSize = sizeof(metrics);
134         SystemParametersInfo(SPI_GETNONCLIENTMETRICS, metrics.cbSize, &metrics, 0);
135         desc.setOneFamily(metrics.lfSmCaptionFont.lfFaceName);
136
137         desc.setComputedSize(18);
138         
139         Font font = Font(desc, 0, 0);
140         font.update(0);
141
142         GraphicsContext cg(context);
143         cg.setFillColor(Color::black, ColorSpaceDeviceRGB);
144         cg.drawText(font, TextRun(text), IntPoint(aBounds.origin.x + 5, aBounds.origin.y + 17));
145
146         CGContextRestoreGState(context);        
147     }
148
149     CGContextRestoreGState(context);
150
151     owner()->owner()->platformCALayerLayerDidDisplay(owner());
152 }
153
154 void PlatformCALayerWinInternal::internalSetNeedsDisplay(const FloatRect* dirtyRect)
155 {
156     if (dirtyRect) {
157         CGRect rect = *dirtyRect;
158         CACFLayerSetNeedsDisplay(owner()->platformLayer(), &rect);
159     } else
160         CACFLayerSetNeedsDisplay(owner()->platformLayer(), 0);
161 }
162
163 void PlatformCALayerWinInternal::setNeedsDisplay()
164 {
165     internalSetNeedsDisplay(0);
166 }
167
168 void PlatformCALayerWinInternal::setNeedsDisplayInRect(const FloatRect& dirtyRect)
169 {
170     if (layerTypeIsTiled(m_owner->layerType())) {
171         // FIXME: Only setNeedsDisplay for tiles that are currently visible
172         int numTileLayers = tileCount();
173         CGRect rect = dirtyRect;
174         for (int i = 0; i < numTileLayers; ++i)
175             CACFLayerSetNeedsDisplay(tileAtIndex(i), &rect);
176
177         if (m_owner->owner() && m_owner->owner()->platformCALayerShowRepaintCounter(m_owner)) {
178             CGRect layerBounds = m_owner->bounds();
179             CGRect indicatorRect = CGRectMake(layerBounds.origin.x, layerBounds.origin.y, 80, 25);
180             CACFLayerSetNeedsDisplay(tileAtIndex(0), &indicatorRect);
181         }
182     } else if (owner()->layerType() == PlatformCALayer::LayerTypeWebLayer) {
183         if (owner() && owner()->owner()) {
184             if (owner()->owner()->platformCALayerShowRepaintCounter(owner())) {
185                 FloatRect layerBounds = owner()->bounds();
186                 FloatRect repaintCounterRect = layerBounds;
187
188                 // We assume a maximum of 4 digits and a font size of 18.
189                 repaintCounterRect.setWidth(80);
190                 repaintCounterRect.setHeight(22);
191                 if (owner()->owner()->platformCALayerContentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesTopDown)
192                     repaintCounterRect.setY(layerBounds.height() - (layerBounds.y() + repaintCounterRect.height()));
193                 internalSetNeedsDisplay(&repaintCounterRect);
194             }
195             if (owner()->owner()->platformCALayerContentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesTopDown) {
196                 FloatRect flippedDirtyRect = dirtyRect;
197                 flippedDirtyRect.setY(owner()->bounds().height() - (flippedDirtyRect.y() + flippedDirtyRect.height()));
198                 internalSetNeedsDisplay(&flippedDirtyRect);
199                 return;
200             }
201         }
202
203         internalSetNeedsDisplay(&dirtyRect);
204     }
205     owner()->setNeedsCommit();
206 }
207
208 void PlatformCALayerWinInternal::setSublayers(const PlatformCALayerList& list)
209 {
210     // Remove all the current sublayers and add the passed layers
211     CACFLayerSetSublayers(owner()->platformLayer(), 0);
212
213     // Perform removeFromSuperLayer in a separate pass. CACF requires superlayer to
214     // be null or CACFLayerInsertSublayer silently fails.
215     for (size_t i = 0; i < list.size(); i++)
216         CACFLayerRemoveFromSuperlayer(list[i]->platformLayer());
217
218     for (size_t i = 0; i < list.size(); i++)
219         CACFLayerInsertSublayer(owner()->platformLayer(), list[i]->platformLayer(), i);
220
221     owner()->setNeedsCommit();
222
223     if (layerTypeIsTiled(m_owner->layerType())) {
224         // Preserve the tile parent after set
225         CACFLayerInsertSublayer(owner()->platformLayer(), m_tileParent.get(), 0);
226     }
227 }
228
229 void PlatformCALayerWinInternal::getSublayers(PlatformCALayerList& list) const
230 {
231     CFArrayRef sublayers = CACFLayerGetSublayers(owner()->platformLayer());
232     if (!sublayers) {
233         list.clear();
234         return;
235     }
236
237     size_t count = CFArrayGetCount(sublayers);
238
239     size_t layersToSkip = 0;
240     if (layerTypeIsTiled(m_owner->layerType())) {
241         // Exclude the tile parent layer.
242         layersToSkip = 1;
243     }
244
245     list.resize(count - layersToSkip);
246     for (size_t arrayIndex = layersToSkip; arrayIndex < count; ++arrayIndex)
247         list[arrayIndex - layersToSkip] = PlatformCALayer::platformCALayer(const_cast<void*>(CFArrayGetValueAtIndex(sublayers, arrayIndex)));
248 }
249
250 void PlatformCALayerWinInternal::removeAllSublayers()
251 {
252     CACFLayerSetSublayers(owner()->platformLayer(), 0);
253     owner()->setNeedsCommit();
254
255     if (layerTypeIsTiled(m_owner->layerType())) {
256         // Restore the tile parent after removal
257         CACFLayerInsertSublayer(owner()->platformLayer(), m_tileParent.get(), 0);
258     }
259 }
260
261 void PlatformCALayerWinInternal::insertSublayer(PlatformCALayer& layer, size_t index)
262 {
263     index = min(index, sublayerCount());
264     if (layerTypeIsTiled(m_owner->layerType())) {
265         // Add 1 to account for the tile parent layer
266         ++index;
267     }
268
269     layer.removeFromSuperlayer();
270     CACFLayerInsertSublayer(owner()->platformLayer(), layer.platformLayer(), index);
271     owner()->setNeedsCommit();
272 }
273
274 size_t PlatformCALayerWinInternal::sublayerCount() const
275 {
276     CFArrayRef sublayers = CACFLayerGetSublayers(owner()->platformLayer());
277     size_t count = sublayers ? CFArrayGetCount(sublayers) : 0;
278
279     if (layerTypeIsTiled(m_owner->layerType())) {
280         // Subtract 1 to account for the tile parent layer
281         ASSERT(count > 0);
282         count--;
283     }
284
285     return count;
286 }
287
288 int PlatformCALayerWinInternal::indexOfSublayer(const PlatformCALayer* reference)
289 {
290     CACFLayerRef ref = reference->platformLayer();
291     if (!ref)
292         return -1;
293
294     CFArrayRef sublayers = CACFLayerGetSublayers(owner()->platformLayer());
295     if (!sublayers)
296         return -1;
297
298     size_t n = CFArrayGetCount(sublayers);
299
300     if (layerTypeIsTiled(m_owner->layerType())) {
301         for (size_t i = 1; i < n; ++i) {
302             if (CFArrayGetValueAtIndex(sublayers, i) == ref)
303                 return i - 1;
304         }
305     } else {
306         for (size_t i = 0; i < n; ++i) {
307             if (CFArrayGetValueAtIndex(sublayers, i) == ref)
308                 return i;
309         }
310     }
311
312     return -1;
313 }
314
315 PlatformCALayer* PlatformCALayerWinInternal::sublayerAtIndex(int index) const
316 {
317     if (layerTypeIsTiled(m_owner->layerType())) {
318         // Add 1 to account for the tile parent layer
319         index++;
320     }
321
322     CFArrayRef sublayers = CACFLayerGetSublayers(owner()->platformLayer());
323     if (!sublayers || index < 0 || CFArrayGetCount(sublayers) <= index)
324         return 0;
325     
326     return PlatformCALayer::platformCALayer(static_cast<CACFLayerRef>(const_cast<void*>(CFArrayGetValueAtIndex(sublayers, index))));
327 }
328
329 void PlatformCALayerWinInternal::setBounds(const FloatRect& rect)
330 {
331     if (CGRectEqualToRect(rect, owner()->bounds()))
332         return;
333
334     CACFLayerSetBounds(owner()->platformLayer(), rect);
335     owner()->setNeedsCommit();
336
337     if (layerTypeIsTiled(m_owner->layerType())) {
338         m_constrainedSize = constrainedSize(rect.size());
339         updateTiles();
340     }
341 }
342
343 void PlatformCALayerWinInternal::setFrame(const FloatRect& rect)
344 {
345     CGRect oldFrame = CACFLayerGetFrame(owner()->platformLayer());
346     if (CGRectEqualToRect(rect, oldFrame))
347         return;
348
349     CACFLayerSetFrame(owner()->platformLayer(), rect);
350     owner()->setNeedsCommit();
351
352     if (layerTypeIsTiled(m_owner->layerType()))
353         updateTiles();
354 }
355
356 CGSize PlatformCALayerWinInternal::constrainedSize(const CGSize& size) const
357 {
358     const int cMaxTileCount = 512;
359     const float cSqrtMaxTileCount = sqrtf(cMaxTileCount);
360
361     CGSize constrainedSize = size;
362
363     int tileColumns = ceilf(constrainedSize.width / m_tileSize.width);
364     int tileRows = ceilf(constrainedSize.height / m_tileSize.height);
365
366     bool tooManyTiles = tileColumns && numeric_limits<int>::max() / tileColumns < tileRows || tileColumns * tileRows > cMaxTileCount;
367
368     // If number of tiles vertically or horizontally is < sqrt(cMaxTileCount)
369     // just shorten the longer dimension. Otherwise shorten both dimensions
370     // according to the ratio of width to height
371
372     if (tooManyTiles) {
373         if (tileRows < cSqrtMaxTileCount)
374             tileColumns = floorf(cMaxTileCount / tileRows);
375         else if (tileColumns < cSqrtMaxTileCount)
376             tileRows = floorf(cMaxTileCount / tileColumns);
377         else {
378             tileRows = ceilf(sqrtf(cMaxTileCount * constrainedSize.height / constrainedSize.width));
379             tileColumns = floorf(cMaxTileCount / tileRows);
380         }
381         
382         constrainedSize.width = tileColumns * m_tileSize.width;
383         constrainedSize.height = tileRows * m_tileSize.height;
384     }
385     
386     return constrainedSize;
387 }
388
389 void PlatformCALayerWinInternal::tileDisplayCallback(CACFLayerRef layer, CGContextRef context)
390 {
391     static_cast<PlatformCALayerWinInternal*>(CACFLayerGetUserData(layer))->drawTile(layer, context);
392 }
393
394 void PlatformCALayerWinInternal::addTile()
395 {
396     RetainPtr<CACFLayerRef> newLayer = adoptCF(CACFLayerCreate(kCACFLayer));
397     CACFLayerSetAnchorPoint(newLayer.get(), CGPointMake(0, 1));
398     CACFLayerSetUserData(newLayer.get(), this);
399     CACFLayerSetDisplayCallback(newLayer.get(), tileDisplayCallback);
400
401     CFArrayRef sublayers = CACFLayerGetSublayers(m_tileParent.get());
402     CACFLayerInsertSublayer(m_tileParent.get(), newLayer.get(), sublayers ? CFArrayGetCount(sublayers) : 0);
403
404     if (owner()->owner()->platformCALayerShowDebugBorders()) {
405         CGColorRef borderColor = CGColorCreateGenericRGB(0.5, 0, 0.5, 0.7);
406         CACFLayerSetBorderColor(newLayer.get(), borderColor);
407         CGColorRelease(borderColor);
408         CACFLayerSetBorderWidth(newLayer.get(), 2);
409     }
410 }
411
412 void PlatformCALayerWinInternal::removeTile()
413 {
414     CACFLayerRemoveFromSuperlayer(tileAtIndex(tileCount() - 1));
415 }
416
417 CACFLayerRef PlatformCALayerWinInternal::tileAtIndex(int index)
418 {
419     CFArrayRef sublayers = CACFLayerGetSublayers(m_tileParent.get());
420     if (!sublayers || index < 0 || index >= tileCount())
421         return 0;
422     
423     return static_cast<CACFLayerRef>(const_cast<void*>(CFArrayGetValueAtIndex(sublayers, index)));
424 }
425
426 int PlatformCALayerWinInternal::tileCount() const
427 {
428     CFArrayRef sublayers = CACFLayerGetSublayers(m_tileParent.get());
429     return sublayers ? CFArrayGetCount(sublayers) : 0;
430 }
431
432 void PlatformCALayerWinInternal::updateTiles()
433 {
434     // FIXME: In addition to redoing the number of tiles, we need to only render and have backing
435     // store for visible layers
436     int numTilesHorizontal = ceil(m_constrainedSize.width / m_tileSize.width);
437     int numTilesVertical = ceil(m_constrainedSize.height / m_tileSize.height);
438     int numTilesTotal = numTilesHorizontal * numTilesVertical;
439     ASSERT(!m_constrainedSize.height || !m_constrainedSize.width || numTilesTotal > 0);
440
441     int numTilesToChange = numTilesTotal - tileCount();
442     if (numTilesToChange >= 0) {
443         // Add new tiles
444         for (int i = 0; i < numTilesToChange; ++i)
445             addTile();
446     } else {
447         // Remove old tiles
448         numTilesToChange = -numTilesToChange;
449         for (int i = 0; i < numTilesToChange; ++i)
450             removeTile();
451     }
452
453     // Set coordinates for all tiles
454     CFArrayRef tileArray = CACFLayerGetSublayers(m_tileParent.get());
455
456     for (int i = 0; i < numTilesHorizontal; ++i) {
457         for (int j = 0; j < numTilesVertical; ++j) {
458             CACFLayerRef tile = static_cast<CACFLayerRef>(const_cast<void*>(CFArrayGetValueAtIndex(tileArray, i * numTilesVertical + j)));
459             CACFLayerSetPosition(tile, CGPointMake(i * m_tileSize.width, j * m_tileSize.height));
460             int width = min(m_tileSize.width, m_constrainedSize.width - i * m_tileSize.width);
461             int height = min(m_tileSize.height, m_constrainedSize.height - j * m_tileSize.height);
462             CACFLayerSetBounds(tile, CGRectMake(i * m_tileSize.width, j * m_tileSize.height, width, height));
463
464             // Flip Y to compensate for the flipping that happens during render to match the CG context coordinate space
465             CATransform3D transform = CATransform3DMakeScale(1, -1, 1);
466             CATransform3DTranslate(transform, 0, height, 0);
467             CACFLayerSetTransform(tile, transform);
468
469 #ifndef NDEBUG
470             String name = "Tile (" + String::number(i) + "," + String::number(j) + ")";
471             CACFLayerSetName(tile, name.createCFString().get());
472 #endif
473         }
474     }
475 }
476
477 void PlatformCALayerWinInternal::drawTile(CACFLayerRef tile, CGContextRef context)
478 {
479     CGPoint tilePosition = CACFLayerGetPosition(tile);
480     CGRect tileBounds = CACFLayerGetBounds(tile);
481
482     CGContextSaveGState(context);
483
484     // Transform context to be at the origin of the parent layer
485     CGContextTranslateCTM(context, -tilePosition.x, -tilePosition.y);
486
487     // Set the context clipping rectangle to the current tile
488     CGContextClipToRect(context, CGRectMake(tilePosition.x, tilePosition.y, tileBounds.size.width, tileBounds.size.height));
489
490     if (owner()->owner()->platformCALayerContentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesTopDown) {
491         // If the layer is rendering top-down, it will flip the coordinates in y. Tiled layers are
492         // already flipping, so we need to undo that here.
493         CGContextTranslateCTM(context, 0, owner()->bounds().height());
494         CGContextScaleCTM(context, 1, -1);
495     }
496
497     // Draw the tile
498     displayCallback(owner()->platformLayer(), context);
499
500     CGContextRestoreGState(context);
501 }
502
503 TileController* PlatformCALayerWinInternal::createTileController(PlatformCALayer* rootLayer)
504 {
505     ASSERT(!m_tileController);
506     m_tileController = TileController::create(rootLayer);
507     return m_tileController.get();
508 }
509
510 TiledBacking* PlatformCALayerWinInternal::tiledBacking()
511 {
512     return m_tileController.get();
513 }