#include "config.h"
#if USE(ACCELERATED_COMPOSITING)
#include "WebTiledLayer.h"
#include "GraphicsLayer.h"
#include "WKCACFLayerRenderer.h"
namespace WebCore {
using namespace std;
void WebTiledLayer::tileDisplayCallback(CACFLayerRef layer, CGContextRef context)
{
static_cast<WebTiledLayer*>(CACFLayerGetUserData(layer))->drawTile(layer, context);
}
PassRefPtr<WebTiledLayer> WebTiledLayer::create(const CGSize& tileSize, GraphicsLayer* owner)
{
ASSERT(WKCACFLayerRenderer::acceleratedCompositingAvailable());
return adoptRef(new WebTiledLayer(tileSize, owner));
}
WebTiledLayer::WebTiledLayer(const CGSize& tileSize, GraphicsLayer* owner)
: WebLayer(WKCACFLayer::Layer, owner)
, m_tileSize(tileSize)
, m_constrainedSize(constrainedSize(bounds().size))
{
m_tileParent.adoptCF(CACFLayerCreate(kCACFLayer));
CACFLayerInsertSublayer(layer(), m_tileParent.get(), 0);
updateTiles();
}
WebTiledLayer::~WebTiledLayer()
{
}
void WebTiledLayer::setBounds(const CGRect& rect)
{
if (CGRectEqualToRect(rect, bounds()))
return;
WebLayer::setBounds(rect);
m_constrainedSize = constrainedSize(rect.size);
updateTiles();
}
void WebTiledLayer::setFrame(const CGRect& rect)
{
if (CGRectEqualToRect(rect, frame()))
return;
WebLayer::setFrame(rect);
updateTiles();
}
void WebTiledLayer::internalSetNeedsDisplay(const CGRect* dirtyRect)
{
int numTileLayers = tileCount();
for (int i = 0; i < numTileLayers; ++i)
CACFLayerSetNeedsDisplay(tileAtIndex(i), dirtyRect);
if (m_owner->showRepaintCounter()) {
CGRect layerBounds = bounds();
CGRect indicatorRect = CGRectMake(layerBounds.origin.x, layerBounds.origin.y, 80, 25);
CACFLayerSetNeedsDisplay(tileAtIndex(0), &indicatorRect);
}
}
size_t WebTiledLayer::internalSublayerCount() const
{
ASSERT(WebLayer::internalSublayerCount() > 0);
return WebLayer::internalSublayerCount() - 1;
}
void WebTiledLayer::internalRemoveAllSublayers()
{
WebLayer::internalRemoveAllSublayers();
CACFLayerInsertSublayer(layer(), m_tileParent.get(), 0);
}
void WebTiledLayer::internalSetSublayers(const Vector<RefPtr<WKCACFLayer> >& sublayers)
{
WebLayer::internalSetSublayers(sublayers);
CACFLayerInsertSublayer(layer(), m_tileParent.get(), 0);
}
void WebTiledLayer::internalInsertSublayer(PassRefPtr<WKCACFLayer> layer, size_t index)
{
WebLayer::internalInsertSublayer(layer, index + 1);
}
WKCACFLayer* WebTiledLayer::internalSublayerAtIndex(int i) const
{
return WebLayer::internalSublayerAtIndex(i + 1);
}
int WebTiledLayer::internalIndexOfSublayer(const WKCACFLayer* layer)
{
int i = WebLayer::internalIndexOfSublayer(layer);
return (i > 0) ? i - 1 : -1;
}
CGSize WebTiledLayer::constrainedSize(const CGSize& size) const
{
const int cMaxTileCount = 512;
const float cSqrtMaxTileCount = sqrtf(cMaxTileCount);
CGSize constrainedSize = size;
int tileColumns = ceilf(constrainedSize.width / m_tileSize.width);
int tileRows = ceilf(constrainedSize.height / m_tileSize.height);
int numTiles = tileColumns * tileRows;
if (numTiles > cMaxTileCount) {
if (tileRows < cSqrtMaxTileCount)
tileColumns = floorf(cMaxTileCount / tileRows);
else if (tileColumns < cSqrtMaxTileCount)
tileRows = floorf(cMaxTileCount / tileColumns);
else {
tileRows = ceilf(sqrtf(cMaxTileCount * constrainedSize.height / constrainedSize.width));
tileColumns = floorf(cMaxTileCount / tileRows);
}
constrainedSize.width = tileColumns * m_tileSize.width;
constrainedSize.height = tileRows * m_tileSize.height;
}
return constrainedSize;
}
void WebTiledLayer::addTile()
{
RetainPtr<CACFLayerRef> newLayer(AdoptCF, CACFLayerCreate(kCACFLayer));
CACFLayerSetAnchorPoint(newLayer.get(), CGPointMake(0, 1));
CACFLayerSetUserData(newLayer.get(), this);
CACFLayerSetDisplayCallback(newLayer.get(), tileDisplayCallback);
CFArrayRef sublayers = CACFLayerGetSublayers(m_tileParent.get());
CACFLayerInsertSublayer(m_tileParent.get(), newLayer.get(), sublayers ? CFArrayGetCount(sublayers) : 0);
if (m_owner->showDebugBorders()) {
CGColorRef borderColor = createCGColor(Color(128, 0, 128, 180));
CACFLayerSetBorderColor(newLayer.get(), borderColor);
CGColorRelease(borderColor);
CACFLayerSetBorderWidth(newLayer.get(), 2);
}
}
void WebTiledLayer::removeTile()
{
CACFLayerRemoveFromSuperlayer(tileAtIndex(tileCount() - 1));
}
CACFLayerRef WebTiledLayer::tileAtIndex(int index)
{
CFArrayRef sublayers = CACFLayerGetSublayers(m_tileParent.get());
if (!sublayers || index < 0 || index >= tileCount() )
return 0;
return static_cast<CACFLayerRef>(const_cast<void*>(CFArrayGetValueAtIndex(sublayers, index)));
}
int WebTiledLayer::tileCount() const
{
CFArrayRef sublayers = CACFLayerGetSublayers(m_tileParent.get());
return sublayers ? CFArrayGetCount(sublayers) : 0;
}
void WebTiledLayer::updateTiles()
{
int numTilesHorizontal = ceil(m_constrainedSize.width / m_tileSize.width);
int numTilesVertical = ceil(m_constrainedSize.height / m_tileSize.height);
int numTilesTotal = numTilesHorizontal * numTilesVertical;
int numTilesToChange = numTilesTotal - tileCount();
if (numTilesToChange >= 0) {
for (int i = 0; i < numTilesToChange; ++i)
addTile();
} else {
numTilesToChange = -numTilesToChange;
for (int i = 0; i < numTilesToChange; ++i)
removeTile();
}
CFArrayRef tileArray = CACFLayerGetSublayers(m_tileParent.get());
for (int i = 0; i < numTilesHorizontal; ++i) {
for (int j = 0; j < numTilesVertical; ++j) {
CACFLayerRef tile = static_cast<CACFLayerRef>(const_cast<void*>(CFArrayGetValueAtIndex(tileArray, i * numTilesVertical + j)));
CACFLayerSetPosition(tile, CGPointMake(i * m_tileSize.width, j * m_tileSize.height));
int width = min(m_tileSize.width, m_constrainedSize.width - i * m_tileSize.width);
int height = min(m_tileSize.height, m_constrainedSize.height - j * m_tileSize.height);
CACFLayerSetBounds(tile, CGRectMake(i * m_tileSize.width, j * m_tileSize.height, width, height));
CATransform3D transform = CATransform3DMakeScale(1, -1, 1);
CATransform3DTranslate(transform, 0, height, 0);
CACFLayerSetTransform(tile, transform);
#ifndef NDEBUG
String name = "Tile (" + String::number(i) + "," + String::number(j) + ")";
CACFLayerSetName(tile, RetainPtr<CFStringRef>(AdoptCF, name.createCFString()).get());
#endif
}
}
}
void WebTiledLayer::drawTile(CACFLayerRef tile, CGContextRef context)
{
CGPoint tilePosition = CACFLayerGetPosition(tile);
CGRect tileBounds = CACFLayerGetBounds(tile);
CGContextSaveGState(context);
CGContextTranslateCTM(context, -tilePosition.x, -tilePosition.y);
CGContextClipToRect(context, CGRectMake(tilePosition.x, tilePosition.y, tileBounds.size.width, tileBounds.size.height));
if (m_owner->contentsOrientation() == WebCore::GraphicsLayer::CompositingCoordinatesTopDown) {
CGContextTranslateCTM(context, 0, bounds().size.height);
CGContextScaleCTM(context, 1, -1);
}
drawInContext(context);
CGContextRestoreGState(context);
}
}
#endif // USE(ACCELERATED_COMPOSITING)