RenderSVGResourcePattern.cpp [plain text]
#include "config.h"
#if ENABLE(SVG)
#include "RenderSVGResourcePattern.h"
#include "GraphicsContext.h"
#include "PatternAttributes.h"
#include "SVGRenderSupport.h"
namespace WebCore {
RenderSVGResourceType RenderSVGResourcePattern::s_resourceType = PatternResourceType;
RenderSVGResourcePattern::RenderSVGResourcePattern(SVGPatternElement* node)
: RenderSVGResourceContainer(node)
{
}
RenderSVGResourcePattern::~RenderSVGResourcePattern()
{
deleteAllValues(m_pattern);
m_pattern.clear();
}
void RenderSVGResourcePattern::invalidateClients()
{
const HashMap<RenderObject*, PatternData*>::const_iterator end = m_pattern.end();
for (HashMap<RenderObject*, PatternData*>::const_iterator it = m_pattern.begin(); it != end; ++it)
markForLayoutAndResourceInvalidation(it->first);
deleteAllValues(m_pattern);
m_pattern.clear();
}
void RenderSVGResourcePattern::invalidateClient(RenderObject* object)
{
ASSERT(object);
if (!m_pattern.contains(object))
return;
delete m_pattern.take(object);
markForLayoutAndResourceInvalidation(object);
}
bool RenderSVGResourcePattern::applyResource(RenderObject* object, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode)
{
ASSERT(object);
ASSERT(style);
ASSERT(context);
ASSERT(resourceMode != ApplyToDefaultMode);
SVGPatternElement* patternElement = static_cast<SVGPatternElement*>(node());
if (!patternElement)
return false;
patternElement->updateAnimatedSVGAttribute(anyQName());
if (!m_pattern.contains(object))
m_pattern.set(object, new PatternData);
PatternData* patternData = m_pattern.get(object);
if (!patternData->pattern) {
FloatRect patternBoundaries;
AffineTransform patternTransform;
OwnPtr<ImageBuffer> tileImage = createTileImage(patternBoundaries, patternTransform, patternElement, object);
if (!tileImage)
return false;
buildPattern(patternData, patternBoundaries, tileImage.release());
if (!patternData->pattern)
return false;
AffineTransform transform;
transform.translate(patternBoundaries.x(), patternBoundaries.y());
transform.multiply(patternTransform);
patternData->pattern->setPatternSpaceTransform(transform);
}
context->save();
const SVGRenderStyle* svgStyle = style->svgStyle();
ASSERT(svgStyle);
if (resourceMode & ApplyToFillMode) {
context->setAlpha(svgStyle->fillOpacity());
context->setFillPattern(patternData->pattern);
context->setFillRule(svgStyle->fillRule());
} else if (resourceMode & ApplyToStrokeMode) {
context->setAlpha(svgStyle->strokeOpacity());
context->setStrokePattern(patternData->pattern);
applyStrokeStyleToContext(context, style, object);
}
if (resourceMode & ApplyToTextMode) {
if (resourceMode & ApplyToFillMode) {
context->setTextDrawingMode(cTextFill);
#if PLATFORM(CG)
context->applyFillPattern();
#endif
} else if (resourceMode & ApplyToStrokeMode) {
context->setTextDrawingMode(cTextStroke);
#if PLATFORM(CG)
context->applyStrokePattern();
#endif
}
}
return true;
}
void RenderSVGResourcePattern::postApplyResource(RenderObject*, GraphicsContext*& context, unsigned short resourceMode)
{
ASSERT(context);
ASSERT(resourceMode != ApplyToDefaultMode);
if (!(resourceMode & ApplyToTextMode)) {
if (resourceMode & ApplyToFillMode)
context->fillPath();
else if (resourceMode & ApplyToStrokeMode)
context->strokePath();
}
context->restore();
}
static inline FloatRect calculatePatternBoundaries(PatternAttributes& attributes,
const FloatRect& objectBoundingBox,
const SVGPatternElement* patternElement)
{
if (attributes.boundingBoxMode())
return FloatRect(attributes.x().valueAsPercentage() * objectBoundingBox.width(),
attributes.y().valueAsPercentage() * objectBoundingBox.height(),
attributes.width().valueAsPercentage() * objectBoundingBox.width(),
attributes.height().valueAsPercentage() * objectBoundingBox.height());
return FloatRect(attributes.x().value(patternElement),
attributes.y().value(patternElement),
attributes.width().value(patternElement),
attributes.height().value(patternElement));
}
FloatRect RenderSVGResourcePattern::calculatePatternBoundariesIncludingOverflow(PatternAttributes& attributes,
const FloatRect& objectBoundingBox,
const AffineTransform& viewBoxCTM,
const FloatRect& patternBoundaries) const
{
FloatRect patternContentBoundaries;
const RenderStyle* style = this->style();
if (style->overflowX() == OVISIBLE && style->overflowY() == OVISIBLE) {
for (Node* node = attributes.patternContentElement()->firstChild(); node; node = node->nextSibling()) {
if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyledTransformable() || !node->renderer())
continue;
patternContentBoundaries.unite(node->renderer()->repaintRectInLocalCoordinates());
}
}
if (patternContentBoundaries.isEmpty())
return patternBoundaries;
FloatRect patternBoundariesIncludingOverflow = patternBoundaries;
if (!viewBoxCTM.isIdentity())
patternContentBoundaries = viewBoxCTM.mapRect(patternContentBoundaries);
else if (attributes.boundingBoxModeContent())
patternContentBoundaries = FloatRect(patternContentBoundaries.x() * objectBoundingBox.width(),
patternContentBoundaries.y() * objectBoundingBox.height(),
patternContentBoundaries.width() * objectBoundingBox.width(),
patternContentBoundaries.height() * objectBoundingBox.height());
patternBoundariesIncludingOverflow.unite(patternContentBoundaries);
return patternBoundariesIncludingOverflow;
}
PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(FloatRect& patternBoundaries,
AffineTransform& patternTransform,
const SVGPatternElement* patternElement,
RenderObject* object) const
{
PatternAttributes attributes = patternElement->collectPatternProperties();
if (!attributes.patternContentElement())
return 0;
FloatRect objectBoundingBox = object->objectBoundingBox();
patternBoundaries = calculatePatternBoundaries(attributes, objectBoundingBox, patternElement);
patternTransform = attributes.patternTransform();
AffineTransform viewBoxCTM = patternElement->viewBoxToViewTransform(patternElement->viewBox(),
patternElement->preserveAspectRatio(),
patternBoundaries.width(),
patternBoundaries.height());
FloatRect patternBoundariesIncludingOverflow = calculatePatternBoundariesIncludingOverflow(attributes,
objectBoundingBox,
viewBoxCTM,
patternBoundaries);
IntSize imageSize(lroundf(patternBoundariesIncludingOverflow.width()), lroundf(patternBoundariesIncludingOverflow.height()));
clampImageBufferSizeToViewport(object->document()->view(), imageSize);
if (imageSize.isEmpty())
return 0;
OwnPtr<ImageBuffer> tileImage = ImageBuffer::create(imageSize);
GraphicsContext* context = tileImage->context();
ASSERT(context);
context->save();
if (patternBoundariesIncludingOverflow.location() != patternBoundaries.location()) {
context->translate(patternBoundaries.x() - patternBoundariesIncludingOverflow.x(),
patternBoundaries.y() - patternBoundariesIncludingOverflow.y());
patternBoundaries.setLocation(patternBoundariesIncludingOverflow.location());
}
if (!viewBoxCTM.isIdentity())
context->concatCTM(viewBoxCTM);
else if (attributes.boundingBoxModeContent()) {
context->translate(objectBoundingBox.x(), objectBoundingBox.y());
context->scale(FloatSize(objectBoundingBox.width(), objectBoundingBox.height()));
}
for (Node* node = attributes.patternContentElement()->firstChild(); node; node = node->nextSibling()) {
if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyled() || !node->renderer())
continue;
renderSubtreeToImage(tileImage.get(), node->renderer());
}
context->restore();
return tileImage.release();
}
void RenderSVGResourcePattern::buildPattern(PatternData* patternData, const FloatRect& patternBoundaries, PassOwnPtr<ImageBuffer> tileImage) const
{
if (!tileImage->image()) {
patternData->pattern = 0;
return;
}
IntRect tileRect = tileImage->image()->rect();
if (tileRect.width() <= patternBoundaries.width() && tileRect.height() <= patternBoundaries.height()) {
patternData->pattern = Pattern::create(tileImage->image(), true, true);
return;
}
int tileWidth = static_cast<int>(patternBoundaries.width() + 0.5f);
int tileHeight = static_cast<int>(patternBoundaries.height() + 0.5f);
if (!tileWidth || !tileHeight) {
patternData->pattern = 0;
return;
}
OwnPtr<ImageBuffer> newTileImage = ImageBuffer::create(IntSize(tileWidth, tileHeight));
GraphicsContext* newTileImageContext = newTileImage->context();
int numY = static_cast<int>(ceilf(tileRect.height() / tileHeight)) + 1;
int numX = static_cast<int>(ceilf(tileRect.width() / tileWidth)) + 1;
newTileImageContext->save();
newTileImageContext->translate(-patternBoundaries.width() * numX, -patternBoundaries.height() * numY);
for (int i = numY; i > 0; --i) {
newTileImageContext->translate(0, patternBoundaries.height());
for (int j = numX; j > 0; --j) {
newTileImageContext->translate(patternBoundaries.width(), 0);
newTileImageContext->drawImage(tileImage->image(), style()->colorSpace(), tileRect, tileRect);
}
newTileImageContext->translate(-patternBoundaries.width() * numX, 0);
}
newTileImageContext->restore();
patternData->pattern = Pattern::create(newTileImage->image(), true, true);
}
}
#endif