1/* 2 * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org> 3 * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org> 4 * Copyright (C) 2005 Eric Seidel <eric@webkit.org> 5 * Copyright (C) 2010 Dirk Schulze <krit@webkit.org> 6 * Copyright (C) 2013 Google Inc. All rights reserved. 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Library General Public 10 * License as published by the Free Software Foundation; either 11 * version 2 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Library General Public License for more details. 17 * 18 * You should have received a copy of the GNU Library General Public License 19 * along with this library; see the file COPYING.LIB. If not, write to 20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 * Boston, MA 02110-1301, USA. 22 */ 23 24#include "config.h" 25 26#include "core/svg/graphics/filters/SVGFEImage.h" 27 28#include "SkBitmapSource.h" 29#include "SkPictureImageFilter.h" 30#include "core/rendering/RenderObject.h" 31#include "core/rendering/svg/SVGRenderingContext.h" 32#include "core/svg/SVGElement.h" 33#include "core/svg/SVGURIReference.h" 34#include "platform/graphics/DisplayList.h" 35#include "platform/graphics/GraphicsContext.h" 36#include "platform/graphics/filters/Filter.h" 37#include "platform/graphics/filters/SkiaImageFilterBuilder.h" 38#include "platform/text/TextStream.h" 39#include "platform/transforms/AffineTransform.h" 40 41namespace blink { 42 43FEImage::FEImage(Filter* filter, PassRefPtr<Image> image, PassRefPtr<SVGPreserveAspectRatio> preserveAspectRatio) 44 : FilterEffect(filter) 45 , m_image(image) 46 , m_treeScope(0) 47 , m_preserveAspectRatio(preserveAspectRatio) 48{ 49} 50 51FEImage::FEImage(Filter* filter, TreeScope& treeScope, const String& href, PassRefPtr<SVGPreserveAspectRatio> preserveAspectRatio) 52 : FilterEffect(filter) 53 , m_treeScope(&treeScope) 54 , m_href(href) 55 , m_preserveAspectRatio(preserveAspectRatio) 56{ 57} 58 59PassRefPtr<FEImage> FEImage::createWithImage(Filter* filter, PassRefPtr<Image> image, PassRefPtr<SVGPreserveAspectRatio> preserveAspectRatio) 60{ 61 return adoptRef(new FEImage(filter, image, preserveAspectRatio)); 62} 63 64PassRefPtr<FEImage> FEImage::createWithIRIReference(Filter* filter, TreeScope& treeScope, const String& href, PassRefPtr<SVGPreserveAspectRatio> preserveAspectRatio) 65{ 66 return adoptRef(new FEImage(filter, treeScope, href, preserveAspectRatio)); 67} 68 69static FloatRect getRendererRepaintRect(RenderObject* renderer) 70{ 71 return renderer->localToParentTransform().mapRect( 72 renderer->paintInvalidationRectInLocalCoordinates()); 73} 74 75AffineTransform makeMapBetweenRects(const FloatRect& source, const FloatRect& dest) 76{ 77 AffineTransform transform; 78 transform.translate(dest.x() - source.x(), dest.y() - source.y()); 79 transform.scale(dest.width() / source.width(), dest.height() / source.height()); 80 return transform; 81} 82 83FloatRect FEImage::determineAbsolutePaintRect(const FloatRect& originalRequestedRect) 84{ 85 RenderObject* renderer = referencedRenderer(); 86 if (!m_image && !renderer) 87 return FloatRect(); 88 89 FloatRect requestedRect = originalRequestedRect; 90 if (clipsToBounds()) 91 requestedRect.intersect(maxEffectRect()); 92 93 FloatRect destRect = filter()->mapLocalRectToAbsoluteRect(filterPrimitiveSubregion()); 94 FloatRect srcRect; 95 if (renderer) { 96 srcRect = getRendererRepaintRect(renderer); 97 SVGElement* contextNode = toSVGElement(renderer->node()); 98 99 if (contextNode->hasRelativeLengths()) { 100 // FIXME: This fixes relative lengths but breaks non-relative ones (see crbug/260709). 101 SVGLengthContext lengthContext(contextNode); 102 FloatSize viewportSize; 103 if (lengthContext.determineViewport(viewportSize)) { 104 srcRect = makeMapBetweenRects(FloatRect(FloatPoint(), viewportSize), destRect).mapRect(srcRect); 105 } 106 } else { 107 srcRect = filter()->mapLocalRectToAbsoluteRect(srcRect); 108 srcRect.move(destRect.x(), destRect.y()); 109 } 110 destRect.intersect(srcRect); 111 } else { 112 srcRect = FloatRect(FloatPoint(), m_image->size()); 113 m_preserveAspectRatio->transformRect(destRect, srcRect); 114 } 115 116 destRect.intersect(requestedRect); 117 addAbsolutePaintRect(destRect); 118 return destRect; 119} 120 121RenderObject* FEImage::referencedRenderer() const 122{ 123 if (!m_treeScope) 124 return 0; 125 Element* hrefElement = SVGURIReference::targetElementFromIRIString(m_href, *m_treeScope); 126 if (!hrefElement || !hrefElement->isSVGElement()) 127 return 0; 128 return hrefElement->renderer(); 129} 130 131void FEImage::applySoftware() 132{ 133 RenderObject* renderer = referencedRenderer(); 134 if (!m_image && !renderer) 135 return; 136 137 ImageBuffer* resultImage = createImageBufferResult(); 138 if (!resultImage) 139 return; 140 IntPoint paintLocation = absolutePaintRect().location(); 141 resultImage->context()->translate(-paintLocation.x(), -paintLocation.y()); 142 143 // FEImage results are always in ColorSpaceDeviceRGB 144 setResultColorSpace(ColorSpaceDeviceRGB); 145 146 FloatRect destRect = filter()->mapLocalRectToAbsoluteRect(filterPrimitiveSubregion()); 147 FloatRect srcRect; 148 149 if (!renderer) { 150 srcRect = FloatRect(FloatPoint(), m_image->size()); 151 m_preserveAspectRatio->transformRect(destRect, srcRect); 152 153 resultImage->context()->drawImage(m_image.get(), destRect, srcRect); 154 return; 155 } 156 157 SVGElement* contextNode = toSVGElement(renderer->node()); 158 if (contextNode->hasRelativeLengths()) { 159 // FIXME: This fixes relative lengths but breaks non-relative ones (see crbug/260709). 160 SVGLengthContext lengthContext(contextNode); 161 FloatSize viewportSize; 162 163 // If we're referencing an element with percentage units, eg. <rect with="30%"> those values were resolved against the viewport. 164 // Build up a transformation that maps from the viewport space to the filter primitive subregion. 165 if (lengthContext.determineViewport(viewportSize)) 166 resultImage->context()->concatCTM(makeMapBetweenRects(FloatRect(FloatPoint(), viewportSize), destRect)); 167 } else { 168 resultImage->context()->translate(destRect.x(), destRect.y()); 169 resultImage->context()->concatCTM(filter()->absoluteTransform()); 170 } 171 172 AffineTransform contentTransformation; 173 SVGRenderingContext::renderSubtree(resultImage->context(), renderer, contentTransformation); 174} 175 176TextStream& FEImage::externalRepresentation(TextStream& ts, int indent) const 177{ 178 IntSize imageSize; 179 if (m_image) 180 imageSize = m_image->size(); 181 else if (RenderObject* renderer = referencedRenderer()) 182 imageSize = enclosingIntRect(getRendererRepaintRect(renderer)).size(); 183 writeIndent(ts, indent); 184 ts << "[feImage"; 185 FilterEffect::externalRepresentation(ts); 186 ts << " image-size=\"" << imageSize.width() << "x" << imageSize.height() << "\"]\n"; 187 // FIXME: should this dump also object returned by SVGFEImage::image() ? 188 return ts; 189} 190 191PassRefPtr<SkImageFilter> FEImage::createImageFilterForRenderer(RenderObject* renderer, SkiaImageFilterBuilder* builder) 192{ 193 FloatRect dstRect = filterPrimitiveSubregion(); 194 195 AffineTransform transform; 196 SVGElement* contextNode = toSVGElement(renderer->node()); 197 198 if (contextNode->hasRelativeLengths()) { 199 SVGLengthContext lengthContext(contextNode); 200 FloatSize viewportSize; 201 202 // If we're referencing an element with percentage units, eg. <rect with="30%"> those values were resolved against the viewport. 203 // Build up a transformation that maps from the viewport space to the filter primitive subregion. 204 if (lengthContext.determineViewport(viewportSize)) 205 transform = makeMapBetweenRects(FloatRect(FloatPoint(), viewportSize), dstRect); 206 } else { 207 transform.translate(dstRect.x(), dstRect.y()); 208 } 209 210 GraphicsContext* context = builder->context(); 211 if (!context) 212 return adoptRef(SkBitmapSource::Create(SkBitmap())); 213 AffineTransform contentTransformation; 214 FloatRect bounds(FloatPoint(), dstRect.size()); 215 context->save(); 216 context->beginRecording(bounds); 217 context->concatCTM(transform); 218 SVGRenderingContext::renderSubtree(context, renderer, contentTransformation); 219 RefPtr<DisplayList> displayList = context->endRecording(); 220 context->restore(); 221 RefPtr<SkImageFilter> result = adoptRef(SkPictureImageFilter::Create(displayList->picture(), dstRect)); 222 return result.release(); 223} 224 225PassRefPtr<SkImageFilter> FEImage::createImageFilter(SkiaImageFilterBuilder* builder) 226{ 227 RenderObject* renderer = referencedRenderer(); 228 if (!m_image && !renderer) 229 return adoptRef(SkBitmapSource::Create(SkBitmap())); 230 231 setOperatingColorSpace(ColorSpaceDeviceRGB); 232 233 if (renderer) 234 return createImageFilterForRenderer(renderer, builder); 235 236 FloatRect srcRect = FloatRect(FloatPoint(), m_image->size()); 237 FloatRect dstRect = filterPrimitiveSubregion(); 238 239 // FIXME: CSS image filters currently do not seem to set filter primitive 240 // subregion correctly if unspecified. So default to srcRect size if so. 241 if (dstRect.isEmpty()) 242 dstRect = srcRect; 243 244 m_preserveAspectRatio->transformRect(dstRect, srcRect); 245 246 if (!m_image->nativeImageForCurrentFrame()) 247 return adoptRef(SkBitmapSource::Create(SkBitmap())); 248 249 RefPtr<SkImageFilter> result = adoptRef(SkBitmapSource::Create(m_image->nativeImageForCurrentFrame()->bitmap(), srcRect, dstRect)); 250 return result.release(); 251} 252 253} // namespace blink 254