Home | History | Annotate | Download | only in win
      1 /*
      2  * Copyright (C) 2009 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 "WebTiledLayer.h"
     31 
     32 #include "GraphicsLayer.h"
     33 #include "WKCACFLayerRenderer.h"
     34 
     35 namespace WebCore {
     36 
     37 using namespace std;
     38 
     39 #ifndef NDEBUG
     40 void WebTiledLayer::internalCheckLayerConsistency()
     41 {
     42     WKCACFLayer::internalCheckLayerConsistency();
     43 
     44     // Additionally make sure the tiled parent is valid
     45     CFArrayRef sublayers = CACFLayerGetSublayers(layer());
     46 
     47     // Make sure there is a tile parent and it is the same as we remember
     48     size_t n = CFArrayGetCount(sublayers);
     49     ASSERT(n > 0);
     50     const void* element = CFArrayGetValueAtIndex(sublayers, 0);
     51     ASSERT(m_tileParent.get() == element);
     52 
     53     // Make sure the tile parent doesn't have user data. If it does, it is probably
     54     // a WKCACFLayer in the wrong place.
     55     ASSERT(!layer(m_tileParent.get()));
     56 }
     57 #endif
     58 
     59 void WebTiledLayer::tileDisplayCallback(CACFLayerRef layer, CGContextRef context)
     60 {
     61     static_cast<WebTiledLayer*>(CACFLayerGetUserData(layer))->drawTile(layer, context);
     62 }
     63 
     64 PassRefPtr<WebTiledLayer> WebTiledLayer::create(const CGSize& tileSize, GraphicsLayer* owner)
     65 {
     66     ASSERT(WKCACFLayerRenderer::acceleratedCompositingAvailable());
     67     return adoptRef(new WebTiledLayer(tileSize, owner));
     68 }
     69 
     70 WebTiledLayer::WebTiledLayer(const CGSize& tileSize, GraphicsLayer* owner)
     71     : WebLayer(WKCACFLayer::Layer, owner)
     72     , m_tileSize(tileSize)
     73     , m_constrainedSize(constrainedSize(bounds().size))
     74 {
     75     // Tiled layers are placed in a child layer that is always the first child of the TiledLayer
     76     m_tileParent.adoptCF(CACFLayerCreate(kCACFLayer));
     77     CACFLayerInsertSublayer(layer(), m_tileParent.get(), 0);
     78 
     79     updateTiles();
     80 }
     81 
     82 WebTiledLayer::~WebTiledLayer()
     83 {
     84 }
     85 
     86 void WebTiledLayer::setBounds(const CGRect& rect)
     87 {
     88     if (CGRectEqualToRect(rect, bounds()))
     89         return;
     90 
     91     WebLayer::setBounds(rect);
     92     m_constrainedSize = constrainedSize(rect.size);
     93     updateTiles();
     94 }
     95 
     96 void WebTiledLayer::setFrame(const CGRect& rect)
     97 {
     98     if (CGRectEqualToRect(rect, frame()))
     99         return;
    100 
    101     WebLayer::setFrame(rect);
    102     updateTiles();
    103 }
    104 
    105 void WebTiledLayer::internalSetNeedsDisplay(const CGRect* dirtyRect)
    106 {
    107     // FIXME: Only setNeedsDisplay for tiles that are currently visible
    108     int numTileLayers = tileCount();
    109     for (int i = 0; i < numTileLayers; ++i)
    110         CACFLayerSetNeedsDisplay(tileAtIndex(i), dirtyRect);
    111 
    112     if (m_owner->showRepaintCounter()) {
    113         CGRect layerBounds = bounds();
    114         CGRect indicatorRect = CGRectMake(layerBounds.origin.x, layerBounds.origin.y, 80, 25);
    115         CACFLayerSetNeedsDisplay(tileAtIndex(0), &indicatorRect);
    116     }
    117 }
    118 
    119 size_t WebTiledLayer::internalSublayerCount() const
    120 {
    121     ASSERT(WebLayer::internalSublayerCount() > 0);
    122 
    123     // Subtract 1 to account for the tile parent layer
    124     return WebLayer::internalSublayerCount() - 1;
    125 }
    126 
    127 void WebTiledLayer::internalRemoveAllSublayers()
    128 {
    129     // Restore the tile parent after removal
    130     WebLayer::internalRemoveAllSublayers();
    131     CACFLayerInsertSublayer(layer(), m_tileParent.get(), 0);
    132 }
    133 
    134 void WebTiledLayer::internalSetSublayers(const Vector<RefPtr<WKCACFLayer> >& sublayers)
    135 {
    136     // Preserve the tile parent after set
    137     WebLayer::internalSetSublayers(sublayers);
    138     CACFLayerInsertSublayer(layer(), m_tileParent.get(), 0);
    139 }
    140 
    141 void WebTiledLayer::internalInsertSublayer(PassRefPtr<WKCACFLayer> layer, size_t index)
    142 {
    143     // Add 1 to account for the tile parent layer
    144     WebLayer::internalInsertSublayer(layer, index + 1);
    145 }
    146 
    147 WKCACFLayer* WebTiledLayer::internalSublayerAtIndex(int i) const
    148 {
    149     // Add 1 to account for the tile parent layer
    150     return WebLayer::internalSublayerAtIndex(i + 1);
    151 }
    152 
    153 int WebTiledLayer::internalIndexOfSublayer(const WKCACFLayer* layer)
    154 {
    155     int i = WebLayer::internalIndexOfSublayer(layer);
    156 
    157     // Add 1 to account for the tile parent layer (but be safe about it)
    158     return (i > 0) ? i - 1 : -1;
    159 }
    160 
    161 CGSize WebTiledLayer::constrainedSize(const CGSize& size) const
    162 {
    163     const int cMaxTileCount = 512;
    164     const float cSqrtMaxTileCount = sqrtf(cMaxTileCount);
    165 
    166     CGSize constrainedSize = size;
    167 
    168     int tileColumns = ceilf(constrainedSize.width / m_tileSize.width);
    169     int tileRows = ceilf(constrainedSize.height / m_tileSize.height);
    170     int numTiles = tileColumns * tileRows;
    171 
    172     // If number of tiles vertically or horizontally is < sqrt(cMaxTileCount)
    173     // just shorten the longer dimension. Otherwise shorten both dimensions
    174     // according to the ratio of width to height
    175 
    176     if (numTiles > cMaxTileCount) {
    177         if (tileRows < cSqrtMaxTileCount)
    178             tileColumns = floorf(cMaxTileCount / tileRows);
    179         else if (tileColumns < cSqrtMaxTileCount)
    180             tileRows = floorf(cMaxTileCount / tileColumns);
    181         else {
    182             tileRows = ceilf(sqrtf(cMaxTileCount * constrainedSize.height / constrainedSize.width));
    183             tileColumns = floorf(cMaxTileCount / tileRows);
    184         }
    185 
    186         constrainedSize.width = tileColumns * m_tileSize.width;
    187         constrainedSize.height = tileRows * m_tileSize.height;
    188     }
    189 
    190     return constrainedSize;
    191 }
    192 
    193 void WebTiledLayer::addTile()
    194 {
    195     RetainPtr<CACFLayerRef> newLayer(AdoptCF, CACFLayerCreate(kCACFLayer));
    196     CACFLayerSetAnchorPoint(newLayer.get(), CGPointMake(0, 1));
    197     CACFLayerSetUserData(newLayer.get(), this);
    198     CACFLayerSetDisplayCallback(newLayer.get(), tileDisplayCallback);
    199 
    200     CFArrayRef sublayers = CACFLayerGetSublayers(m_tileParent.get());
    201     CACFLayerInsertSublayer(m_tileParent.get(), newLayer.get(), sublayers ? CFArrayGetCount(sublayers) : 0);
    202 
    203     if (m_owner->showDebugBorders()) {
    204         CGColorRef borderColor = CGColorCreateGenericRGB(0.5, 0, 0.5, 0.7);
    205         CACFLayerSetBorderColor(newLayer.get(), borderColor);
    206         CGColorRelease(borderColor);
    207         CACFLayerSetBorderWidth(newLayer.get(), 2);
    208     }
    209 }
    210 
    211 void WebTiledLayer::removeTile()
    212 {
    213     CACFLayerRemoveFromSuperlayer(tileAtIndex(tileCount() - 1));
    214 }
    215 
    216 CACFLayerRef WebTiledLayer::tileAtIndex(int index)
    217 {
    218     CFArrayRef sublayers = CACFLayerGetSublayers(m_tileParent.get());
    219     if (!sublayers || index < 0 || index >= tileCount() )
    220         return 0;
    221 
    222     return static_cast<CACFLayerRef>(const_cast<void*>(CFArrayGetValueAtIndex(sublayers, index)));
    223 }
    224 
    225 int WebTiledLayer::tileCount() const
    226 {
    227     CFArrayRef sublayers = CACFLayerGetSublayers(m_tileParent.get());
    228     return sublayers ? CFArrayGetCount(sublayers) : 0;
    229 }
    230 
    231 void WebTiledLayer::updateTiles()
    232 {
    233     // FIXME: In addition to redoing the number of tiles, we need to only render and have backing
    234     // store for visible layers
    235     int numTilesHorizontal = ceil(m_constrainedSize.width / m_tileSize.width);
    236     int numTilesVertical = ceil(m_constrainedSize.height / m_tileSize.height);
    237     int numTilesTotal = numTilesHorizontal * numTilesVertical;
    238 
    239     int numTilesToChange = numTilesTotal - tileCount();
    240     if (numTilesToChange >= 0) {
    241         // Add new tiles
    242         for (int i = 0; i < numTilesToChange; ++i)
    243             addTile();
    244     } else {
    245         // Remove old tiles
    246         numTilesToChange = -numTilesToChange;
    247         for (int i = 0; i < numTilesToChange; ++i)
    248             removeTile();
    249     }
    250 
    251     // Set coordinates for all tiles
    252     CFArrayRef tileArray = CACFLayerGetSublayers(m_tileParent.get());
    253 
    254     for (int i = 0; i < numTilesHorizontal; ++i) {
    255         for (int j = 0; j < numTilesVertical; ++j) {
    256             CACFLayerRef tile = static_cast<CACFLayerRef>(const_cast<void*>(CFArrayGetValueAtIndex(tileArray, i * numTilesVertical + j)));
    257             CACFLayerSetPosition(tile, CGPointMake(i * m_tileSize.width, j * m_tileSize.height));
    258             int width = min(m_tileSize.width, m_constrainedSize.width - i * m_tileSize.width);
    259             int height = min(m_tileSize.height, m_constrainedSize.height - j * m_tileSize.height);
    260             CACFLayerSetBounds(tile, CGRectMake(i * m_tileSize.width, j * m_tileSize.height, width, height));
    261 
    262             // Flip Y to compensate for the flipping that happens during render to match the CG context coordinate space
    263             CATransform3D transform = CATransform3DMakeScale(1, -1, 1);
    264             CATransform3DTranslate(transform, 0, height, 0);
    265             CACFLayerSetTransform(tile, transform);
    266 
    267 #ifndef NDEBUG
    268             String name = "Tile (" + String::number(i) + "," + String::number(j) + ")";
    269             CACFLayerSetName(tile, RetainPtr<CFStringRef>(AdoptCF, name.createCFString()).get());
    270 #endif
    271         }
    272     }
    273 }
    274 
    275 void WebTiledLayer::drawTile(CACFLayerRef tile, CGContextRef context)
    276 {
    277     CGPoint tilePosition = CACFLayerGetPosition(tile);
    278     CGRect tileBounds = CACFLayerGetBounds(tile);
    279 
    280     CGContextSaveGState(context);
    281 
    282     // Transform context to be at the origin of the parent layer
    283     CGContextTranslateCTM(context, -tilePosition.x, -tilePosition.y);
    284 
    285     // Set the context clipping rectangle to the current tile
    286     CGContextClipToRect(context, CGRectMake(tilePosition.x, tilePosition.y, tileBounds.size.width, tileBounds.size.height));
    287 
    288     if (m_owner->contentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesTopDown) {
    289         // If the layer is rendering top-down, it will flip the coordinates in y. Tiled layers are
    290         // already flipping, so we need to undo that here.
    291         CGContextTranslateCTM(context, 0, bounds().size.height);
    292         CGContextScaleCTM(context, 1, -1);
    293     }
    294 
    295     // Draw the tile
    296     drawInContext(context);
    297 
    298     CGContextRestoreGState(context);
    299 }
    300 
    301 }
    302 
    303 #endif // USE(ACCELERATED_COMPOSITING)
    304