Reviewed by Eric. Rubber stamped by Oliver.
[WebKit-https.git] / WebCore / platform / graphics / svg / cg / SVGResourceFilterCg.mm
1 /*
2  * Copyright (C) 2005 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27
28 #ifdef SVG_SUPPORT
29 #include "AffineTransform.h"
30 #include "FoundationExtras.h"
31 #include "GraphicsContext.h"
32 #include "SVGResourceFilter.h"
33
34 #include "SVGFEBlend.h"
35 #include "SVGFEColorMatrix.h"
36 #include "SVGFEComponentTransfer.h"
37 #include "SVGFEComposite.h"
38 #include "SVGFEDiffuseLighting.h"
39 #include "SVGFEDisplacementMap.h"
40 #include "SVGFEFlood.h"
41 #include "SVGFEGaussianBlur.h"
42 #include "SVGFEImage.h"
43 #include "SVGFEMerge.h"
44 #include "SVGFEOffset.h"
45 #include "SVGFESpecularLighting.h"
46 #include "SVGFETile.h"
47
48 #include <QuartzCore/CoreImage.h>
49
50 namespace WebCore {
51
52 static const char* const SVGPreviousFilterOutputName = "__previousOutput__";
53
54 SVGResourceFilter::SVGResourceFilter()
55     : m_filterCIContext(0)
56     , m_filterCGLayer(0)
57     , m_savedContext(0)
58 {
59     m_imagesByName = HardRetainWithNSRelease([[NSMutableDictionary alloc] init]);
60 }
61
62 SVGResourceFilter::~SVGResourceFilter()
63 {
64     ASSERT(!m_filterCGLayer);
65     ASSERT(!m_filterCIContext);
66     HardRelease(m_imagesByName);
67 }
68
69 SVGFilterEffect* SVGResourceFilter::createFilterEffect(const SVGFilterEffectType& type)
70 {
71     switch(type)
72     {
73     /* Light sources are contained by the diffuse/specular light blocks 
74     case FE_DISTANT_LIGHT: 
75     case FE_POINT_LIGHT: 
76     case FE_SPOT_LIGHT: 
77     */
78     case FE_BLEND: return new SVGFEBlend();
79     case FE_COLOR_MATRIX: return new SVGFEColorMatrix();
80     case FE_COMPONENT_TRANSFER: return new SVGFEComponentTransfer();
81     case FE_COMPOSITE: return new SVGFEComposite();
82 //  case FE_CONVOLVE_MATRIX:
83     case FE_DIFFUSE_LIGHTING: return new SVGFEDiffuseLighting();
84     case FE_DISPLACEMENT_MAP: return new SVGFEDisplacementMap();
85     case FE_FLOOD: return new SVGFEFlood();
86     case FE_GAUSSIAN_BLUR: return new SVGFEGaussianBlur();
87     case FE_IMAGE: return new SVGFEImage();
88     case FE_MERGE: return new SVGFEMerge();
89 //  case FE_MORPHOLOGY:
90     case FE_OFFSET: return new SVGFEOffset();
91     case FE_SPECULAR_LIGHTING: return new SVGFESpecularLighting();
92     case FE_TILE: return new SVGFETile();
93 //  case FE_TURBULENCE:
94     default:
95         return 0;
96     }
97 }
98
99 void SVGResourceFilter::prepareFilter(GraphicsContext*& context, const FloatRect& bbox)
100 {
101     if (bbox.isEmpty() || m_effects.isEmpty())
102         return;
103
104     CGContextRef cgContext = context->platformContext();
105
106     // Use of CGBegin/EndTransparencyLayer around this call causes over release
107     // of cgContext due to it being created on an autorelease pool, and released
108     // after CGEndTransparencyLayer. Create local pool to fix.
109     // <http://bugs.webkit.org/show_bug.cgi?id=8425>
110     // <http://bugs.webkit.org/show_bug.cgi?id=6947>
111     // <rdar://problem/4647735>
112     NSAutoreleasePool* filterContextPool = [[NSAutoreleasePool alloc] init];
113     m_filterCIContext = HardRetain([CIContext contextWithCGContext:cgContext options:nil]);
114     [filterContextPool drain];
115
116     m_filterCGLayer = [m_filterCIContext createCGLayerWithSize:CGRect(bbox).size info:NULL];
117     m_savedContext = context;
118
119     context = new GraphicsContext(CGLayerGetContext(m_filterCGLayer));
120     context->save();
121     context->concatCTM(AffineTransform().translate(-1.0f * bbox.x(), -1.0f * bbox.y()));
122 }
123
124 void SVGResourceFilter::applyFilter(GraphicsContext*& context, const FloatRect& bbox)
125 {
126     if (bbox.isEmpty() || m_effects.isEmpty())
127         return;
128
129     // actually apply the filter effects
130     CIImage* inputImage = [CIImage imageWithCGLayer:m_filterCGLayer];
131     NSArray* filterStack = getCIFilterStack(inputImage);
132     if ([filterStack count]) {
133         CIImage* outputImage = [[filterStack lastObject] valueForKey:@"outputImage"];
134         if (outputImage) {
135             CGRect filterRect = CGRect(filterBBoxForItemBBox(bbox));
136             CGRect translated = filterRect;
137             CGPoint bboxOrigin = CGRect(bbox).origin;
138             CGRect sourceRect = CGRectIntersection(translated,[outputImage extent]);
139
140             CGPoint destOrigin = sourceRect.origin;
141             destOrigin.x += bboxOrigin.x;
142             destOrigin.y += bboxOrigin.y;
143
144             [m_filterCIContext drawImage:outputImage atPoint:destOrigin fromRect:sourceRect];
145         }
146     }
147
148     CGLayerRelease(m_filterCGLayer);
149     m_filterCGLayer = 0;
150
151     HardRelease(m_filterCIContext);
152     m_filterCIContext = 0;
153
154     delete context;
155     context = m_savedContext;
156     m_savedContext = 0;
157 }
158
159 NSArray* SVGResourceFilter::getCIFilterStack(CIImage* inputImage)
160 {
161     NSMutableArray* filterEffects = [NSMutableArray array];
162
163     setImageForName(inputImage, "SourceGraphic"); // input
164
165     for (unsigned int i = 0; i < m_effects.size(); i++) {
166         CIFilter* filter = m_effects[i]->getCIFilter(this);
167         if (filter)
168             [filterEffects addObject:filter];
169     }
170
171     [m_imagesByName removeAllObjects]; // clean up before next time.
172
173     return filterEffects;
174 }
175
176 CIImage *SVGResourceFilter::imageForName(const String& name) const
177 {
178     return [m_imagesByName objectForKey:name];
179 }
180
181 void SVGResourceFilter::setImageForName(CIImage *image, const String &name)
182 {
183     [m_imagesByName setValue:image forKey:name];
184 }
185
186 void SVGResourceFilter::setOutputImage(const SVGFilterEffect *filterEffect, CIImage *output)
187 {
188     if (!filterEffect->result().isEmpty())
189         setImageForName(output, filterEffect->result());
190     setImageForName(output, SVGPreviousFilterOutputName);
191 }
192
193 static inline CIImage *alphaImageForImage(CIImage *image)
194 {
195     CIFilter *onlyAlpha = [CIFilter filterWithName:@"CIColorMatrix"];
196     CGFloat zero[4] = {0, 0, 0, 0};
197     [onlyAlpha setDefaults];
198     [onlyAlpha setValue:image forKey:@"inputImage"];
199     [onlyAlpha setValue:[CIVector vectorWithValues:zero count:4] forKey:@"inputRVector"];
200     [onlyAlpha setValue:[CIVector vectorWithValues:zero count:4] forKey:@"inputGVector"];
201     [onlyAlpha setValue:[CIVector vectorWithValues:zero count:4] forKey:@"inputBVector"];
202     return [onlyAlpha valueForKey:@"outputImage"];
203 }
204
205 CIImage *SVGResourceFilter::inputImage(const SVGFilterEffect *filterEffect)
206 {
207     if (filterEffect->in().isEmpty()) {
208         CIImage *inImage = imageForName(SVGPreviousFilterOutputName);
209         if (!inImage)
210             inImage = imageForName("SourceGraphic");
211         return inImage;
212     } else if (filterEffect->in() == "SourceAlpha") {
213         CIImage *sourceAlpha = imageForName(filterEffect->in());
214         if (!sourceAlpha) {
215             CIImage *sourceGraphic = imageForName("SourceGraphic");
216             if (!sourceGraphic)
217                 return nil;
218             sourceAlpha = alphaImageForImage(sourceGraphic);
219             setImageForName(sourceAlpha, "SourceAlpha");
220         }
221         return sourceAlpha;
222     }
223
224     return imageForName(filterEffect->in());
225 }
226
227 }
228
229 #endif // SVG_SUPPORT