1 /* 2 * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann (at) kde.org> 3 * Copyright (C) 2004, 2005 Rob Buis <buis (at) kde.org> 4 * Copyright (C) 2005 Eric Seidel <eric (at) webkit.org> 5 * Copyright (C) 2009 Dirk Schulze <krit (at) webkit.org> 6 * Copyright (C) Research In Motion Limited 2010. 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/rendering/svg/RenderSVGResourceFilter.h" 27 28 #include "core/dom/ElementTraversal.h" 29 #include "core/frame/Settings.h" 30 #include "core/rendering/svg/RenderSVGResourceFilterPrimitive.h" 31 #include "core/rendering/svg/SVGRenderingContext.h" 32 #include "core/svg/SVGFilterPrimitiveStandardAttributes.h" 33 #include "platform/graphics/UnacceleratedImageBufferSurface.h" 34 #include "platform/graphics/filters/SkiaImageFilterBuilder.h" 35 #include "platform/graphics/filters/SourceAlpha.h" 36 #include "platform/graphics/filters/SourceGraphic.h" 37 38 namespace blink { 39 40 const RenderSVGResourceType RenderSVGResourceFilter::s_resourceType = FilterResourceType; 41 42 RenderSVGResourceFilter::RenderSVGResourceFilter(SVGFilterElement* node) 43 : RenderSVGResourceContainer(node) 44 { 45 } 46 47 RenderSVGResourceFilter::~RenderSVGResourceFilter() 48 { 49 } 50 51 void RenderSVGResourceFilter::destroy() 52 { 53 m_filter.clear(); 54 RenderSVGResourceContainer::destroy(); 55 } 56 57 bool RenderSVGResourceFilter::isChildAllowed(RenderObject* child, RenderStyle*) const 58 { 59 return child->isSVGResourceFilterPrimitive(); 60 } 61 62 void RenderSVGResourceFilter::removeAllClientsFromCache(bool markForInvalidation) 63 { 64 m_filter.clear(); 65 markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation); 66 } 67 68 void RenderSVGResourceFilter::removeClientFromCache(RenderObject* client, bool markForInvalidation) 69 { 70 ASSERT(client); 71 72 m_filter.remove(client); 73 74 markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation); 75 } 76 77 PassRefPtr<SVGFilterBuilder> RenderSVGResourceFilter::buildPrimitives(SVGFilter* filter) 78 { 79 SVGFilterElement* filterElement = toSVGFilterElement(element()); 80 FloatRect targetBoundingBox = filter->targetBoundingBox(); 81 82 // Add effects to the builder 83 RefPtr<SVGFilterBuilder> builder = SVGFilterBuilder::create(SourceGraphic::create(filter), SourceAlpha::create(filter)); 84 for (SVGElement* element = Traversal<SVGElement>::firstChild(*filterElement); element; element = Traversal<SVGElement>::nextSibling(*element)) { 85 if (!element->isFilterEffect() || !element->renderer()) 86 continue; 87 88 SVGFilterPrimitiveStandardAttributes* effectElement = static_cast<SVGFilterPrimitiveStandardAttributes*>(element); 89 RefPtr<FilterEffect> effect = effectElement->build(builder.get(), filter); 90 if (!effect) { 91 builder->clearEffects(); 92 return nullptr; 93 } 94 builder->appendEffectToEffectReferences(effect, effectElement->renderer()); 95 effectElement->setStandardAttributes(effect.get()); 96 effect->setEffectBoundaries(SVGLengthContext::resolveRectangle<SVGFilterPrimitiveStandardAttributes>(effectElement, filterElement->primitiveUnits()->currentValue()->enumValue(), targetBoundingBox)); 97 effect->setOperatingColorSpace( 98 effectElement->renderer()->style()->svgStyle().colorInterpolationFilters() == CI_LINEARRGB ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB); 99 builder->add(AtomicString(effectElement->result()->currentValue()->value()), effect); 100 } 101 return builder.release(); 102 } 103 104 static void beginDeferredFilter(GraphicsContext* context, FilterData* filterData) 105 { 106 context->beginRecording(filterData->boundaries); 107 context->setShouldSmoothFonts(false); 108 // We pass the boundaries to SkPictureImageFilter so it knows the 109 // world-space position of the filter primitives. It gets them 110 // from the DisplayList, which also applies the inverse translate 111 // to the origin. So we apply the forward translate here to avoid 112 // it being applied twice. 113 // FIXME: we should fix SkPicture to handle this offset itself, or 114 // make the translate optional on SkPictureImageFilter. 115 // See https://code.google.com/p/skia/issues/detail?id=2801 116 context->translate(filterData->boundaries.x(), filterData->boundaries.y()); 117 } 118 119 static void endDeferredFilter(GraphicsContext* context, FilterData* filterData) 120 { 121 // FIXME: maybe filterData should just hold onto SourceGraphic after creation? 122 SourceGraphic* sourceGraphic = static_cast<SourceGraphic*>(filterData->builder->getEffectById(SourceGraphic::effectName())); 123 ASSERT(sourceGraphic); 124 sourceGraphic->setDisplayList(context->endRecording()); 125 } 126 127 static void drawDeferredFilter(GraphicsContext* context, FilterData* filterData, SVGFilterElement* filterElement) 128 { 129 SkiaImageFilterBuilder builder(context); 130 SourceGraphic* sourceGraphic = static_cast<SourceGraphic*>(filterData->builder->getEffectById(SourceGraphic::effectName())); 131 ASSERT(sourceGraphic); 132 builder.setSourceGraphic(sourceGraphic); 133 RefPtr<ImageFilter> imageFilter = builder.build(filterData->builder->lastEffect(), ColorSpaceDeviceRGB); 134 FloatRect boundaries = filterData->boundaries; 135 context->save(); 136 137 FloatSize deviceSize = context->getCTM().mapSize(boundaries.size()); 138 float scaledArea = deviceSize.width() * deviceSize.height(); 139 140 // If area of scaled size is bigger than the upper limit, adjust the scale 141 // to fit. Note that this only really matters in the non-impl-side painting 142 // case, since the impl-side case never allocates a full-sized backing 143 // store, only tile-sized. 144 // FIXME: remove this once all platforms are using impl-side painting. 145 // crbug.com/169282. 146 if (scaledArea > FilterEffect::maxFilterArea()) { 147 float scale = sqrtf(FilterEffect::maxFilterArea() / scaledArea); 148 context->scale(scale, scale); 149 } 150 // Clip drawing of filtered image to the minimum required paint rect. 151 FilterEffect* lastEffect = filterData->builder->lastEffect(); 152 context->clipRect(lastEffect->determineAbsolutePaintRect(lastEffect->maxEffectRect())); 153 if (filterElement->hasAttribute(SVGNames::filterResAttr)) { 154 // Get boundaries in device coords. 155 // FIXME: See crbug.com/382491. Is the use of getCTM OK here, given it does not include device 156 // zoom or High DPI adjustments? 157 FloatSize size = context->getCTM().mapSize(boundaries.size()); 158 // Compute the scale amount required so that the resulting offscreen is exactly filterResX by filterResY pixels. 159 float filterResScaleX = filterElement->filterResX()->currentValue()->value() / size.width(); 160 float filterResScaleY = filterElement->filterResY()->currentValue()->value() / size.height(); 161 // Scale the CTM so the primitive is drawn to filterRes. 162 context->scale(filterResScaleX, filterResScaleY); 163 // Create a resize filter with the inverse scale. 164 AffineTransform resizeMatrix; 165 resizeMatrix.scale(1 / filterResScaleX, 1 / filterResScaleY); 166 imageFilter = builder.buildTransform(resizeMatrix, imageFilter.get()); 167 } 168 // If the CTM contains rotation or shearing, apply the filter to 169 // the unsheared/unrotated matrix, and do the shearing/rotation 170 // as a final pass. 171 AffineTransform ctm = context->getCTM(); 172 if (ctm.b() || ctm.c()) { 173 AffineTransform scaleAndTranslate; 174 scaleAndTranslate.translate(ctm.e(), ctm.f()); 175 scaleAndTranslate.scale(ctm.xScale(), ctm.yScale()); 176 ASSERT(scaleAndTranslate.isInvertible()); 177 AffineTransform shearAndRotate = scaleAndTranslate.inverse(); 178 shearAndRotate.multiply(ctm); 179 context->setCTM(scaleAndTranslate); 180 imageFilter = builder.buildTransform(shearAndRotate, imageFilter.get()); 181 } 182 context->beginLayer(1, CompositeSourceOver, &boundaries, ColorFilterNone, imageFilter.get()); 183 context->endLayer(); 184 context->restore(); 185 } 186 187 bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode) 188 { 189 ASSERT(object); 190 ASSERT(context); 191 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode); 192 193 clearInvalidationMask(); 194 195 if (m_filter.contains(object)) { 196 FilterData* filterData = m_filter.get(object); 197 if (filterData->state == FilterData::PaintingSource || filterData->state == FilterData::Applying) 198 filterData->state = FilterData::CycleDetected; 199 return false; // Already built, or we're in a cycle, or we're marked for removal. Regardless, just do nothing more now. 200 } 201 202 OwnPtr<FilterData> filterData(adoptPtr(new FilterData)); 203 FloatRect targetBoundingBox = object->objectBoundingBox(); 204 205 SVGFilterElement* filterElement = toSVGFilterElement(element()); 206 filterData->boundaries = SVGLengthContext::resolveRectangle<SVGFilterElement>(filterElement, filterElement->filterUnits()->currentValue()->enumValue(), targetBoundingBox); 207 if (filterData->boundaries.isEmpty()) 208 return false; 209 210 filterData->drawingRegion = object->strokeBoundingBox(); 211 filterData->drawingRegion.intersect(filterData->boundaries); 212 IntRect intDrawingRegion = enclosingIntRect(filterData->drawingRegion); 213 214 // Create the SVGFilter object. 215 bool primitiveBoundingBoxMode = filterElement->primitiveUnits()->currentValue()->enumValue() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX; 216 filterData->filter = SVGFilter::create(intDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode); 217 218 // Create all relevant filter primitives. 219 filterData->builder = buildPrimitives(filterData->filter.get()); 220 if (!filterData->builder) 221 return false; 222 223 FilterEffect* lastEffect = filterData->builder->lastEffect(); 224 if (!lastEffect) 225 return false; 226 227 lastEffect->determineFilterPrimitiveSubregion(ClipToFilterRegion); 228 229 FilterData* data = filterData.get(); 230 m_filter.set(object, filterData.release()); 231 beginDeferredFilter(context, data); 232 return true; 233 } 234 235 void RenderSVGResourceFilter::postApplyResource(RenderObject* object, GraphicsContext*& context) 236 { 237 ASSERT(object); 238 ASSERT(context); 239 240 FilterData* filterData = m_filter.get(object); 241 if (!filterData) 242 return; 243 244 switch (filterData->state) { 245 case FilterData::CycleDetected: 246 case FilterData::Applying: 247 // We have a cycle if we are already applying the data. 248 // This can occur due to FeImage referencing a source that makes use of the FEImage itself. 249 // This is the first place we've hit the cycle, so set the state back to PaintingSource so the return stack 250 // will continue correctly. 251 filterData->state = FilterData::PaintingSource; 252 return; 253 254 case FilterData::PaintingSource: 255 endDeferredFilter(context, filterData); 256 break; 257 258 case FilterData::Built: { } // Empty 259 } 260 261 drawDeferredFilter(context, filterData, toSVGFilterElement(element())); 262 filterData->state = FilterData::Built; 263 } 264 265 FloatRect RenderSVGResourceFilter::resourceBoundingBox(const RenderObject* object) 266 { 267 if (SVGFilterElement* element = toSVGFilterElement(this->element())) 268 return SVGLengthContext::resolveRectangle<SVGFilterElement>(element, element->filterUnits()->currentValue()->enumValue(), object->objectBoundingBox()); 269 270 return FloatRect(); 271 } 272 273 void RenderSVGResourceFilter::primitiveAttributeChanged(RenderObject* object, const QualifiedName& attribute) 274 { 275 FilterMap::iterator it = m_filter.begin(); 276 FilterMap::iterator end = m_filter.end(); 277 SVGFilterPrimitiveStandardAttributes* primitve = static_cast<SVGFilterPrimitiveStandardAttributes*>(object->node()); 278 279 for (; it != end; ++it) { 280 FilterData* filterData = it->value.get(); 281 if (filterData->state != FilterData::Built) 282 continue; 283 284 SVGFilterBuilder* builder = filterData->builder.get(); 285 FilterEffect* effect = builder->effectByRenderer(object); 286 if (!effect) 287 continue; 288 // Since all effects shares the same attribute value, all 289 // or none of them will be changed. 290 if (!primitve->setFilterEffectAttribute(effect, attribute)) 291 return; 292 builder->clearResultsRecursive(effect); 293 294 // Issue paint invalidations for the image on the screen. 295 markClientForInvalidation(it->key, PaintInvalidation); 296 } 297 markAllClientLayersForInvalidation(); 298 } 299 300 FloatRect RenderSVGResourceFilter::drawingRegion(RenderObject* object) const 301 { 302 FilterData* filterData = m_filter.get(object); 303 return filterData ? filterData->drawingRegion : FloatRect(); 304 } 305 306 } 307