1/*
2 * Copyright (C) 2008 Alex Mathews <possessedpenguinbob@gmail.com>
3 * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
4 * Copyright (C) 2013 Google Inc. All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB.  If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22#include "config.h"
23
24#include "core/platform/graphics/filters/FETile.h"
25
26#include "SkFlattenableBuffers.h"
27#include "SkImageFilter.h"
28
29#include "core/platform/graphics/GraphicsContext.h"
30#include "core/platform/graphics/Pattern.h"
31#include "core/platform/graphics/filters/Filter.h"
32#include "core/platform/graphics/filters/SkiaImageFilterBuilder.h"
33#include "core/platform/graphics/transforms/AffineTransform.h"
34#include "core/platform/text/TextStream.h"
35#include "core/rendering/RenderTreeAsText.h"
36#include "core/rendering/svg/SVGRenderingContext.h"
37#include "third_party/skia/include/core/SkDevice.h"
38
39namespace WebCore {
40
41class TileImageFilter : public SkImageFilter {
42public:
43    TileImageFilter(const SkRect& srcRect, const SkRect& dstRect, SkImageFilter* input)
44        : SkImageFilter(input)
45        , m_srcRect(srcRect)
46        , m_dstRect(dstRect)
47    {
48    }
49
50    virtual bool onFilterImage(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm, SkBitmap* dst, SkIPoint* offset)
51    {
52        SkBitmap source = src;
53        SkImageFilter* input = getInput(0);
54        SkIPoint localOffset = SkIPoint::Make(0, 0);
55        if (input && !input->filterImage(proxy, src, ctm, &source, &localOffset))
56            return false;
57
58        if (!m_srcRect.width() || !m_srcRect.height() || !m_dstRect.width() || !m_dstRect.height())
59            return false;
60
61        SkIRect srcRect;
62        m_srcRect.roundOut(&srcRect);
63        SkBitmap subset;
64        if (!source.extractSubset(&subset, srcRect))
65            return false;
66
67        SkAutoTUnref<SkDevice> device(proxy->createDevice(m_dstRect.width(), m_dstRect.height()));
68        SkIRect bounds;
69        source.getBounds(&bounds);
70        SkCanvas canvas(device);
71        SkPaint paint;
72        paint.setXfermodeMode(SkXfermode::kSrc_Mode);
73
74        SkAutoTUnref<SkShader> shader(SkShader::CreateBitmapShader(subset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode));
75        paint.setShader(shader);
76        SkRect dstRect = m_dstRect;
77        dstRect.offset(localOffset.fX, localOffset.fY);
78        canvas.drawRect(dstRect, paint);
79        *dst = device->accessBitmap(false);
80        return true;
81    }
82
83    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(TileImageFilter)
84
85protected:
86    explicit TileImageFilter(SkFlattenableReadBuffer& buffer)
87        : SkImageFilter(buffer)
88    {
89        buffer.readRect(&m_srcRect);
90        buffer.readRect(&m_dstRect);
91    }
92
93    virtual void flatten(SkFlattenableWriteBuffer& buffer) const
94    {
95        this->SkImageFilter::flatten(buffer);
96        buffer.writeRect(m_srcRect);
97        buffer.writeRect(m_dstRect);
98    }
99
100private:
101    SkRect m_srcRect;
102    SkRect m_dstRect;
103};
104
105FETile::FETile(Filter* filter)
106    : FilterEffect(filter)
107{
108}
109
110PassRefPtr<FETile> FETile::create(Filter* filter)
111{
112    return adoptRef(new FETile(filter));
113}
114
115void FETile::applySoftware()
116{
117    FilterEffect* in = inputEffect(0);
118
119    ImageBuffer* resultImage = createImageBufferResult();
120    if (!resultImage)
121        return;
122
123    setIsAlphaImage(in->isAlphaImage());
124
125    // Source input needs more attention. It has the size of the filterRegion but gives the
126    // size of the cutted sourceImage back. This is part of the specification and optimization.
127    FloatRect tileRect = in->maxEffectRect();
128    FloatPoint inMaxEffectLocation = tileRect.location();
129    FloatPoint maxEffectLocation = maxEffectRect().location();
130    if (in->filterEffectType() == FilterEffectTypeSourceInput) {
131        Filter* filter = this->filter();
132        tileRect = filter->absoluteFilterRegion();
133        tileRect.scale(filter->filterResolution().width(), filter->filterResolution().height());
134    }
135
136    OwnPtr<ImageBuffer> tileImage;
137    if (!SVGRenderingContext::createImageBufferForPattern(tileRect, tileRect, tileImage, filter()->renderingMode()))
138        return;
139
140    GraphicsContext* tileImageContext = tileImage->context();
141    tileImageContext->translate(-inMaxEffectLocation.x(), -inMaxEffectLocation.y());
142    tileImageContext->drawImageBuffer(in->asImageBuffer(), in->absolutePaintRect().location());
143
144    RefPtr<Pattern> pattern = Pattern::create(tileImage->copyImage(CopyBackingStore), true, true);
145
146    AffineTransform patternTransform;
147    patternTransform.translate(inMaxEffectLocation.x() - maxEffectLocation.x(), inMaxEffectLocation.y() - maxEffectLocation.y());
148    pattern->setPatternSpaceTransform(patternTransform);
149    GraphicsContext* filterContext = resultImage->context();
150    filterContext->setFillPattern(pattern);
151    filterContext->fillRect(FloatRect(FloatPoint(), absolutePaintRect().size()));
152}
153
154PassRefPtr<SkImageFilter> FETile::createImageFilter(SkiaImageFilterBuilder* builder)
155{
156    RefPtr<SkImageFilter> input(builder->build(inputEffect(0), operatingColorSpace()));
157    FloatRect srcRect = inputEffect(0) ? inputEffect(0)->effectBoundaries() : FloatRect();
158    return adoptRef(new TileImageFilter(srcRect, effectBoundaries(), input.get()));
159}
160
161TextStream& FETile::externalRepresentation(TextStream& ts, int indent) const
162{
163    writeIndent(ts, indent);
164    ts << "[feTile";
165    FilterEffect::externalRepresentation(ts);
166    ts << "]\n";
167    inputEffect(0)->externalRepresentation(ts, indent + 1);
168
169    return ts;
170}
171
172} // namespace WebCore
173