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