Home | History | Annotate | Download | only in graphics
      1 /*
      2     Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann (at) kde.org>
      3                   2004, 2005 Rob Buis <buis (at) kde.org>
      4                   2005 Eric Seidel <eric (at) webkit.org>
      5                   2009 Dirk Schulze <krit (at) webkit.org>
      6 
      7     This library is free software; you can redistribute it and/or
      8     modify it under the terms of the GNU Library General Public
      9     License as published by the Free Software Foundation; either
     10     version 2 of the License, or (at your option) any later version.
     11 
     12     This library is distributed in the hope that it will be useful,
     13     but WITHOUT ANY WARRANTY; without even the implied warranty of
     14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15     Library General Public License for more details.
     16 
     17     You should have received a copy of the GNU Library General Public License
     18     aint with this library; see the file COPYING.LIB.  If not, write to
     19     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     20     Boston, MA 02110-1301, USA.
     21 */
     22 
     23 #include "config.h"
     24 
     25 #if ENABLE(SVG) && ENABLE(FILTERS)
     26 #include "SVGResourceFilter.h"
     27 
     28 #include "FilterEffect.h"
     29 #include "GraphicsContext.h"
     30 #include "ImageBuffer.h"
     31 #include "PlatformString.h"
     32 #include "SVGFilter.h"
     33 #include "SVGFilterBuilder.h"
     34 #include "SVGFilterElement.h"
     35 #include "SVGRenderSupport.h"
     36 #include "SVGRenderTreeAsText.h"
     37 #include "SVGFilterPrimitiveStandardAttributes.h"
     38 
     39 static const float kMaxFilterSize = 5000.0f;
     40 
     41 using std::min;
     42 
     43 namespace WebCore {
     44 
     45 SVGResourceFilter::SVGResourceFilter(const SVGFilterElement* ownerElement)
     46     : SVGResource()
     47     , m_ownerElement(ownerElement)
     48     , m_filterBBoxMode(false)
     49     , m_effectBBoxMode(false)
     50     , m_filterRes(false)
     51     , m_scaleX(1.f)
     52     , m_scaleY(1.f)
     53     , m_savedContext(0)
     54     , m_sourceGraphicBuffer(0)
     55 {
     56     m_filterBuilder.set(new SVGFilterBuilder());
     57 }
     58 
     59 SVGResourceFilter::~SVGResourceFilter()
     60 {
     61 }
     62 
     63 FloatRect SVGResourceFilter::filterBoundingBox(const FloatRect& obb) const
     64 {
     65     return m_ownerElement->filterBoundingBox(obb);
     66 }
     67 
     68 static inline bool shouldProcessFilter(SVGResourceFilter* filter, const FloatRect& filterRect)
     69 {
     70     return (!filter->scaleX() || !filter->scaleY() || !filterRect.width() || !filterRect.height());
     71 }
     72 
     73 void SVGResourceFilter::addFilterEffect(SVGFilterPrimitiveStandardAttributes* effectAttributes, PassRefPtr<FilterEffect> effect)
     74 {
     75     effectAttributes->setStandardAttributes(this, effect.get());
     76     builder()->add(effectAttributes->result(), effect);
     77 }
     78 
     79 bool SVGResourceFilter::fitsInMaximumImageSize(const FloatSize& size)
     80 {
     81     bool matchesFilterSize = true;
     82     if (size.width() > kMaxFilterSize) {
     83         m_scaleX *= kMaxFilterSize / size.width();
     84         matchesFilterSize = false;
     85     }
     86     if (size.height() > kMaxFilterSize) {
     87         m_scaleY *= kMaxFilterSize / size.height();
     88         matchesFilterSize = false;
     89     }
     90 
     91     return matchesFilterSize;
     92 }
     93 
     94 bool SVGResourceFilter::prepareFilter(GraphicsContext*& context, const RenderObject* object)
     95 {
     96     m_ownerElement->buildFilter(object->objectBoundingBox());
     97     const SVGRenderBase* renderer = object->toSVGRenderBase();
     98     if (!renderer)
     99         return false;
    100 
    101     FloatRect paintRect = renderer->strokeBoundingBox();
    102     paintRect.unite(renderer->markerBoundingBox());
    103 
    104     if (shouldProcessFilter(this, m_filterBBox))
    105         return false;
    106 
    107     // clip sourceImage to filterRegion
    108     FloatRect clippedSourceRect = paintRect;
    109     clippedSourceRect.intersect(m_filterBBox);
    110 
    111     // scale filter size to filterRes
    112     FloatRect tempSourceRect = clippedSourceRect;
    113     if (m_filterRes) {
    114         m_scaleX = m_filterResSize.width() / m_filterBBox.width();
    115         m_scaleY = m_filterResSize.height() / m_filterBBox.height();
    116     }
    117 
    118     // scale to big sourceImage size to kMaxFilterSize
    119     tempSourceRect.scale(m_scaleX, m_scaleY);
    120     fitsInMaximumImageSize(tempSourceRect.size());
    121 
    122     // prepare Filters
    123     m_filter = SVGFilter::create(paintRect, m_filterBBox, m_effectBBoxMode);
    124     m_filter->setFilterResolution(FloatSize(m_scaleX, m_scaleY));
    125 
    126     FilterEffect* lastEffect = m_filterBuilder->lastEffect();
    127     if (lastEffect) {
    128         lastEffect->calculateEffectRect(m_filter.get());
    129         // at least one FilterEffect has a too big image size,
    130         // recalculate the effect sizes with new scale factors
    131         if (!fitsInMaximumImageSize(m_filter->maxImageSize())) {
    132             m_filter->setFilterResolution(FloatSize(m_scaleX, m_scaleY));
    133             lastEffect->calculateEffectRect(m_filter.get());
    134         }
    135     } else
    136         return false;
    137 
    138     clippedSourceRect.scale(m_scaleX, m_scaleY);
    139 
    140     // Draw the content of the current element and it's childs to a imageBuffer to get the SourceGraphic.
    141     // The size of the SourceGraphic is clipped to the size of the filterRegion.
    142     IntRect bufferRect = enclosingIntRect(clippedSourceRect);
    143     OwnPtr<ImageBuffer> sourceGraphic(ImageBuffer::create(bufferRect.size(), LinearRGB));
    144 
    145     if (!sourceGraphic.get())
    146         return false;
    147 
    148     GraphicsContext* sourceGraphicContext = sourceGraphic->context();
    149     sourceGraphicContext->translate(-clippedSourceRect.x(), -clippedSourceRect.y());
    150     sourceGraphicContext->scale(FloatSize(m_scaleX, m_scaleY));
    151     sourceGraphicContext->clearRect(FloatRect(FloatPoint(), paintRect.size()));
    152     m_sourceGraphicBuffer.set(sourceGraphic.release());
    153     m_savedContext = context;
    154 
    155     context = sourceGraphicContext;
    156     return true;
    157 }
    158 
    159 void SVGResourceFilter::applyFilter(GraphicsContext*& context, const RenderObject* object)
    160 {
    161     if (!m_savedContext)
    162         return;
    163 
    164     context = m_savedContext;
    165     m_savedContext = 0;
    166 #if !PLATFORM(CG)
    167     m_sourceGraphicBuffer->transformColorSpace(DeviceRGB, LinearRGB);
    168 #endif
    169 
    170     FilterEffect* lastEffect = m_filterBuilder->lastEffect();
    171 
    172     if (lastEffect && !m_filterBBox.isEmpty() && !lastEffect->subRegion().isEmpty()) {
    173         m_filter->setSourceImage(m_sourceGraphicBuffer.release());
    174         lastEffect->apply(m_filter.get());
    175 
    176         ImageBuffer* resultImage = lastEffect->resultImage();
    177         if (resultImage) {
    178 #if !PLATFORM(CG)
    179             resultImage->transformColorSpace(LinearRGB, DeviceRGB);
    180 #endif
    181             ColorSpace colorSpace = DeviceColorSpace;
    182             if (object)
    183                 colorSpace = object->style()->colorSpace();
    184             context->drawImage(resultImage->image(), colorSpace, lastEffect->subRegion());
    185         }
    186     }
    187 
    188     m_sourceGraphicBuffer.clear();
    189 }
    190 
    191 TextStream& SVGResourceFilter::externalRepresentation(TextStream& ts) const
    192 {
    193     ts << "[type=FILTER] ";
    194 
    195     FloatRect bbox = filterRect();
    196     static FloatRect defaultFilterRect(0, 0, 1, 1);
    197 
    198     if (!filterBoundingBoxMode() || bbox != defaultFilterRect) {
    199         ts << " [bounding box=";
    200         if (filterBoundingBoxMode()) {
    201             bbox.scale(100.f);
    202             ts << "at (" << bbox.x() << "%," <<  bbox.y() << "%) size " << bbox.width() << "%x" << bbox.height() << "%";
    203         } else
    204             ts << filterRect();
    205         ts << "]";
    206     }
    207 
    208     if (!filterBoundingBoxMode()) // default is true
    209         ts << " [bounding box mode=" << filterBoundingBoxMode() << "]";
    210     if (effectBoundingBoxMode()) // default is false
    211         ts << " [effect bounding box mode=" << effectBoundingBoxMode() << "]";
    212 
    213     return ts;
    214 }
    215 
    216 SVGResourceFilter* getFilterById(Document* document, const AtomicString& id, const RenderObject* object)
    217 {
    218     SVGResource* resource = getResourceById(document, id, object);
    219     if (resource && resource->isFilter())
    220         return static_cast<SVGResourceFilter*>(resource);
    221 
    222     return 0;
    223 }
    224 
    225 
    226 } // namespace WebCore
    227 
    228 #endif // ENABLE(SVG)
    229