blob: 39235986e0504eeb6db9f5b7216018ae9c216d89 [file] [log] [blame]
/*
* Copyright (C) 2009 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#if USE(ACCELERATED_COMPOSITING)
#include "WKCACFLayer.h"
#include "CString.h"
#include "WKCACFContextFlusher.h"
#include "WKCACFLayerRenderer.h"
#include <stdio.h>
#include <QuartzCore/CACFContext.h>
#include <QuartzCore/CARender.h>
#include <QuartzCoreInterface/QuartzCoreInterface.h>
#ifndef NDEBUG
#include <wtf/CurrentTime.h>
#endif
#ifdef DEBUG_ALL
#pragma comment(lib, "QuartzCore_debug")
#pragma comment(lib, "QuartzCoreInterface_debug")
#else
#pragma comment(lib, "QuartzCore")
#pragma comment(lib, "QuartzCoreInterface")
#endif
namespace WebCore {
using namespace std;
static void displayCallback(CACFLayerRef layer, CGContextRef context)
{
ASSERT_ARG(layer, WKCACFLayer::layer(layer));
WKCACFLayer::layer(layer)->drawInContext(context);
}
#define STATIC_CACF_STRING(name) \
static CFStringRef name() \
{ \
static CFStringRef name = wkqcCFStringRef(wkqc##name); \
return name; \
}
STATIC_CACF_STRING(kCACFLayer)
STATIC_CACF_STRING(kCACFTransformLayer)
STATIC_CACF_STRING(kCACFGravityCenter)
STATIC_CACF_STRING(kCACFGravityTop)
STATIC_CACF_STRING(kCACFGravityBottom)
STATIC_CACF_STRING(kCACFGravityLeft)
STATIC_CACF_STRING(kCACFGravityRight)
STATIC_CACF_STRING(kCACFGravityTopLeft)
STATIC_CACF_STRING(kCACFGravityTopRight)
STATIC_CACF_STRING(kCACFGravityBottomLeft)
STATIC_CACF_STRING(kCACFGravityBottomRight)
STATIC_CACF_STRING(kCACFGravityResize)
STATIC_CACF_STRING(kCACFGravityResizeAspect)
STATIC_CACF_STRING(kCACFGravityResizeAspectFill)
STATIC_CACF_STRING(kCACFFilterLinear)
STATIC_CACF_STRING(kCACFFilterNearest)
STATIC_CACF_STRING(kCACFFilterTrilinear)
STATIC_CACF_STRING(kCACFFilterLanczos)
static CFStringRef toCACFLayerType(WKCACFLayer::LayerType type)
{
switch (type) {
case WKCACFLayer::Layer: return kCACFLayer();
case WKCACFLayer::TransformLayer: return kCACFTransformLayer();
default: return 0;
}
}
static CFStringRef toCACFContentsGravityType(WKCACFLayer::ContentsGravityType type)
{
switch (type) {
case WKCACFLayer::Center: return kCACFGravityCenter();
case WKCACFLayer::Top: return kCACFGravityTop();
case WKCACFLayer::Bottom: return kCACFGravityBottom();
case WKCACFLayer::Left: return kCACFGravityLeft();
case WKCACFLayer::Right: return kCACFGravityRight();
case WKCACFLayer::TopLeft: return kCACFGravityTopLeft();
case WKCACFLayer::TopRight: return kCACFGravityTopRight();
case WKCACFLayer::BottomLeft: return kCACFGravityBottomLeft();
case WKCACFLayer::BottomRight: return kCACFGravityBottomRight();
case WKCACFLayer::Resize: return kCACFGravityResize();
case WKCACFLayer::ResizeAspect: return kCACFGravityResizeAspect();
case WKCACFLayer::ResizeAspectFill: return kCACFGravityResizeAspectFill();
default: return 0;
}
}
static WKCACFLayer::ContentsGravityType fromCACFContentsGravityType(CFStringRef string)
{
if (CFEqual(string, kCACFGravityTop()))
return WKCACFLayer::Top;
if (CFEqual(string, kCACFGravityBottom()))
return WKCACFLayer::Bottom;
if (CFEqual(string, kCACFGravityLeft()))
return WKCACFLayer::Left;
if (CFEqual(string, kCACFGravityRight()))
return WKCACFLayer::Right;
if (CFEqual(string, kCACFGravityTopLeft()))
return WKCACFLayer::TopLeft;
if (CFEqual(string, kCACFGravityTopRight()))
return WKCACFLayer::TopRight;
if (CFEqual(string, kCACFGravityBottomLeft()))
return WKCACFLayer::BottomLeft;
if (CFEqual(string, kCACFGravityBottomRight()))
return WKCACFLayer::BottomRight;
if (CFEqual(string, kCACFGravityResize()))
return WKCACFLayer::Resize;
if (CFEqual(string, kCACFGravityResizeAspect()))
return WKCACFLayer::ResizeAspect;
if (CFEqual(string, kCACFGravityResizeAspectFill()))
return WKCACFLayer::ResizeAspectFill;
return WKCACFLayer::Center;
}
static CFStringRef toCACFFilterType(WKCACFLayer::FilterType type)
{
switch (type) {
case WKCACFLayer::Linear: return kCACFFilterLinear();
case WKCACFLayer::Nearest: return kCACFFilterNearest();
case WKCACFLayer::Trilinear: return kCACFFilterTrilinear();
case WKCACFLayer::Lanczos: return kCACFFilterLanczos();
default: return 0;
}
}
static WKCACFLayer::FilterType fromCACFFilterType(CFStringRef string)
{
if (CFEqual(string, kCACFFilterNearest()))
return WKCACFLayer::Nearest;
if (CFEqual(string, kCACFFilterTrilinear()))
return WKCACFLayer::Trilinear;
if (CFEqual(string, kCACFFilterLanczos()))
return WKCACFLayer::Lanczos;
return WKCACFLayer::Linear;
}
PassRefPtr<WKCACFLayer> WKCACFLayer::create(LayerType type)
{
if (!WKCACFLayerRenderer::acceleratedCompositingAvailable())
return 0;
return adoptRef(new WKCACFLayer(type));
}
// FIXME: It might be good to have a way of ensuring that all WKCACFLayers eventually
// get destroyed in debug builds. A static counter could accomplish this pretty easily.
WKCACFLayer::WKCACFLayer(LayerType type)
: m_layer(AdoptCF, CACFLayerCreate(toCACFLayerType(type)))
, m_needsDisplayOnBoundsChange(false)
{
CACFLayerSetUserData(layer(), this);
CACFLayerSetDisplayCallback(layer(), displayCallback);
}
WKCACFLayer::~WKCACFLayer()
{
// Our superlayer should be holding a reference to us, so there should be no way for us to be destroyed while we still have a superlayer.
ASSERT(!superlayer());
CACFLayerSetUserData(layer(), 0);
CACFLayerSetDisplayCallback(layer(), 0);
}
void WKCACFLayer::becomeRootLayerForContext(CACFContextRef context)
{
CACFContextSetLayer(context, layer());
setNeedsCommit();
}
void WKCACFLayer::setNeedsCommit()
{
WKCACFLayer* root = rootLayer();
CACFContextRef context = CACFLayerGetContext(root->layer());
// The context might now be set yet. This happens if a property gets set
// before placing the layer in the tree. In this case we don't need to
// worry about remembering the context because we will when the layer is
// added to the tree.
if (context)
WKCACFContextFlusher::shared().addContext(context);
// Call setNeedsRender on the root layer, which will cause a render to
// happen in WKCACFLayerRenderer
root->setNeedsRender();
}
bool WKCACFLayer::isTransformLayer() const
{
return CACFLayerGetClass(layer()) == kCACFTransformLayer();
}
void WKCACFLayer::addSublayer(PassRefPtr<WKCACFLayer> sublayer)
{
insertSublayer(sublayer, numSublayers());
}
void WKCACFLayer::insertSublayer(PassRefPtr<WKCACFLayer> sublayer, size_t index)
{
index = min(index, numSublayers());
sublayer->removeFromSuperlayer();
CACFLayerInsertSublayer(layer(), sublayer->layer(), index);
setNeedsCommit();
}
void WKCACFLayer::insertSublayerAboveLayer(PassRefPtr<WKCACFLayer> sublayer, const WKCACFLayer* reference)
{
if (!reference) {
insertSublayer(sublayer, 0);
return;
}
int referenceIndex = indexOfSublayer(reference);
if (referenceIndex == -1) {
addSublayer(sublayer);
return;
}
insertSublayer(sublayer, referenceIndex + 1);
}
void WKCACFLayer::insertSublayerBelowLayer(PassRefPtr<WKCACFLayer> sublayer, const WKCACFLayer* reference)
{
if (!reference) {
insertSublayer(sublayer, 0);
return;
}
int referenceIndex = indexOfSublayer(reference);
if (referenceIndex == -1) {
addSublayer(sublayer);
return;
}
insertSublayer(sublayer, referenceIndex);
}
void WKCACFLayer::replaceSublayer(WKCACFLayer* reference, PassRefPtr<WKCACFLayer> newLayer)
{
ASSERT_ARG(reference, reference);
ASSERT_ARG(reference, reference->superlayer() == this);
if (reference == newLayer)
return;
int referenceIndex = indexOfSublayer(reference);
ASSERT(referenceIndex != -1);
if (referenceIndex == -1)
return;
reference->removeFromSuperlayer();
if (newLayer) {
newLayer->removeFromSuperlayer();
insertSublayer(newLayer, referenceIndex);
}
}
void WKCACFLayer::removeFromSuperlayer()
{
WKCACFLayer* superlayer = this->superlayer();
if (!superlayer)
return;
CACFLayerRemoveFromSuperlayer(layer());
superlayer->setNeedsCommit();
}
const WKCACFLayer* WKCACFLayer::sublayerAtIndex(int index) const
{
CFArrayRef sublayers = CACFLayerGetSublayers(layer());
if (index < 0 || CFArrayGetCount(sublayers) <= index)
return 0;
return layer(static_cast<CACFLayerRef>(const_cast<void*>(CFArrayGetValueAtIndex(sublayers, index))));
}
int WKCACFLayer::indexOfSublayer(const WKCACFLayer* reference)
{
CACFLayerRef ref = reference->layer();
if (!ref)
return -1;
CFArrayRef sublayers = CACFLayerGetSublayers(layer());
size_t n = CFArrayGetCount(sublayers);
for (size_t i = 0; i < n; ++i)
if (CFArrayGetValueAtIndex(sublayers, i) == ref)
return i;
return -1;
}
WKCACFLayer* WKCACFLayer::ancestorOrSelfWithSuperlayer(WKCACFLayer* superlayer) const
{
WKCACFLayer* layer = const_cast<WKCACFLayer*>(this);
for (WKCACFLayer* ancestor = this->superlayer(); ancestor; layer = ancestor, ancestor = ancestor->superlayer()) {
if (ancestor == superlayer)
return layer;
}
return 0;
}
void WKCACFLayer::setBounds(const CGRect& rect)
{
if (CGRectEqualToRect(rect, bounds()))
return;
CACFLayerSetBounds(layer(), rect);
setNeedsCommit();
if (m_needsDisplayOnBoundsChange)
setNeedsDisplay();
}
void WKCACFLayer::setFrame(const CGRect& rect)
{
CGRect oldFrame = frame();
if (CGRectEqualToRect(rect, oldFrame))
return;
CACFLayerSetFrame(layer(), rect);
setNeedsCommit();
if (m_needsDisplayOnBoundsChange)
setNeedsDisplay();
}
void WKCACFLayer::setContentsGravity(ContentsGravityType type)
{
CACFLayerSetContentsGravity(layer(), toCACFContentsGravityType(type));
setNeedsCommit();
}
WKCACFLayer::ContentsGravityType WKCACFLayer::contentsGravity() const
{
return fromCACFContentsGravityType(CACFLayerGetContentsGravity(layer()));
}
void WKCACFLayer::setMagnificationFilter(FilterType type)
{
CACFLayerSetMagnificationFilter(layer(), toCACFFilterType(type));
setNeedsCommit();
}
WKCACFLayer::FilterType WKCACFLayer::magnificationFilter() const
{
return fromCACFFilterType(CACFLayerGetMagnificationFilter(layer()));
}
void WKCACFLayer::setMinificationFilter(FilterType type)
{
CACFLayerSetMinificationFilter(layer(), toCACFFilterType(type));
setNeedsCommit();
}
WKCACFLayer::FilterType WKCACFLayer::minificationFilter() const
{
return fromCACFFilterType(CACFLayerGetMinificationFilter(layer()));
}
WKCACFLayer* WKCACFLayer::rootLayer() const
{
WKCACFLayer* layer = const_cast<WKCACFLayer*>(this);
for (WKCACFLayer* superlayer = layer->superlayer(); superlayer; layer = superlayer, superlayer = superlayer->superlayer()) { }
return layer;
}
void WKCACFLayer::removeAllSublayers()
{
CACFLayerSetSublayers(layer(), 0);
setNeedsCommit();
}
void WKCACFLayer::setSublayers(const Vector<RefPtr<WKCACFLayer> >& sublayers)
{
// Remove all the current sublayers and add the passed layers
CACFLayerSetSublayers(layer(), 0);
// Perform removeFromSuperLayer in a separate pass. CACF requires superlayer to
// be null or CACFLayerInsertSublayer silently fails.
for (size_t i = 0; i < sublayers.size(); i++)
CACFLayerRemoveFromSuperlayer(sublayers[i]->layer());
for (size_t i = 0; i < sublayers.size(); i++)
CACFLayerInsertSublayer(layer(), sublayers[i]->layer(), i);
setNeedsCommit();
}
WKCACFLayer* WKCACFLayer::superlayer() const
{
CACFLayerRef super = CACFLayerGetSuperlayer(layer());
if (!super)
return 0;
return WKCACFLayer::layer(super);
}
void WKCACFLayer::setNeedsDisplay(const CGRect* dirtyRect)
{
CACFLayerSetNeedsDisplay(layer(), dirtyRect);
setNeedsCommit();
}
void WKCACFLayer::setNeedsDisplay()
{
setNeedsDisplay(0);
}
#ifndef NDEBUG
static void printIndent(int indent)
{
for ( ; indent > 0; --indent)
fprintf(stderr, " ");
}
static void printTransform(const CATransform3D& transform)
{
fprintf(stderr, "[%g %g %g %g; %g %g %g %g; %g %g %g %g; %g %g %g %g]",
transform.m11, transform.m12, transform.m13, transform.m14,
transform.m21, transform.m22, transform.m23, transform.m24,
transform.m31, transform.m32, transform.m33, transform.m34,
transform.m41, transform.m42, transform.m43, transform.m44);
}
void WKCACFLayer::printTree() const
{
// Print heading info
CGRect rootBounds = bounds();
fprintf(stderr, "\n\n** Render tree at time %g (bounds %g, %g %gx%g) **\n\n",
currentTime(), rootBounds.origin.x, rootBounds.origin.y, rootBounds.size.width, rootBounds.size.height);
// Print layer tree from the root
printLayer(0);
}
void WKCACFLayer::printLayer(int indent) const
{
CGPoint layerPosition = position();
CGPoint layerAnchorPoint = anchorPoint();
CGRect layerBounds = bounds();
printIndent(indent);
fprintf(stderr, "(%s [%g %g %g] [%g %g %g %g] [%g %g %g] superlayer=%p\n",
isTransformLayer() ? "transform-layer" : "layer",
layerPosition.x, layerPosition.y, zPosition(),
layerBounds.origin.x, layerBounds.origin.y, layerBounds.size.width, layerBounds.size.height,
layerAnchorPoint.x, layerAnchorPoint.y, anchorPointZ(), superlayer());
// Print name if needed
String layerName = name();
if (!layerName.isEmpty()) {
printIndent(indent + 1);
fprintf(stderr, "(name %s)\n", layerName.utf8().data());
}
// Print masksToBounds if needed
bool layerMasksToBounds = masksToBounds();
if (layerMasksToBounds) {
printIndent(indent + 1);
fprintf(stderr, "(masksToBounds true)\n");
}
// Print opacity if needed
float layerOpacity = opacity();
if (layerOpacity != 1) {
printIndent(indent + 1);
fprintf(stderr, "(opacity %hf)\n", layerOpacity);
}
// Print sublayerTransform if needed
CATransform3D layerTransform = sublayerTransform();
if (!CATransform3DIsIdentity(layerTransform)) {
printIndent(indent + 1);
fprintf(stderr, "(sublayerTransform ");
printTransform(layerTransform);
fprintf(stderr, ")\n");
}
// Print transform if needed
layerTransform = transform();
if (!CATransform3DIsIdentity(layerTransform)) {
printIndent(indent + 1);
fprintf(stderr, "(transform ");
printTransform(layerTransform);
fprintf(stderr, ")\n");
}
// Print contents if needed
CGImageRef layerContents = contents();
if (layerContents) {
printIndent(indent + 1);
fprintf(stderr, "(contents (image [%d %d]))\n",
CGImageGetWidth(layerContents), CGImageGetHeight(layerContents));
}
// Print sublayers if needed
int n = numSublayers();
if (n > 0) {
printIndent(indent + 1);
fprintf(stderr, "(sublayers\n");
for (int i = 0; i < n; ++i)
sublayerAtIndex(i)->printLayer(indent + 2);
printIndent(indent + 1);
fprintf(stderr, ")\n");
}
printIndent(indent);
fprintf(stderr, ")\n");
}
#endif // #ifndef NDEBUG
}
#endif // USE(ACCELERATED_COMPOSITING)