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