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