Fix by Dave MacLachlan, reviewed by Darin and Alexey.
[WebKit-https.git] / WebCore / kcanvas / device / quartz / KCanvasFilterQuartz.mm
1 /*
2  * Copyright (C) 2005, 2006 Apple Computer, Inc.  All rights reserved.
3  * Copyright (C) 2006 Dave MacLachlan (dmaclach@mac.com)
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25  */
26
27
28 #include "config.h"
29 #if SVG_SUPPORT
30 #import "KCanvasFilterQuartz.h"
31
32 #import "BlockExceptions.h"
33 #import "CachedImage.h"
34 #import "FoundationExtras.h"
35 #import "Image.h"
36 #import "KRenderingDeviceQuartz.h"
37 #import "QuartzSupport.h"
38 #import "WKArithmeticFilter.h"
39 #import "WKDiffuseLightingFilter.h"
40 #import "WKDisplacementMapFilter.h"
41 #import "WKDistantLightFilter.h"
42 #import "WKNormalMapFilter.h"
43 #import "WKArithmeticFilter.h"
44 #import "WKComponentMergeFilter.h"
45 #import "WKIdentityTransferFilter.h"
46 #import "WKTableTransferFilter.h"
47 #import "WKDiscreteTransferFilter.h"
48 #import "WKLinearTransferFilter.h"
49 #import "WKGammaTransferFilter.h"
50 #import "WKPointLightFilter.h"
51 #import "WKSpecularLightingFilter.h"
52 #import "WKSpotLightFilter.h"
53 #import <QuartzCore/QuartzCore.h>
54
55 namespace WebCore {
56
57 static const char* const KCPreviousFilterOutputName = "__previousOutput__";
58
59 static inline CIColor *ciColor(const Color &c)
60 {
61     CGColorRef colorCG = cgColor(c);
62     CIColor *colorCI = [CIColor colorWithCGColor:colorCG];
63     CGColorRelease(colorCG);
64     return colorCI;
65 }
66
67 static inline CIVector *ciVector(KCanvasPoint3F point)
68 {
69     return [CIVector vectorWithX:point.x() Y:point.y() Z:point.z()];
70 }
71
72 static inline CIVector *ciVector(FloatPoint point)
73 {
74     return [CIVector vectorWithX:point.x() Y:point.y()];
75 }
76
77 static inline CIVector *getVectorForChannel(KCChannelSelectorType channel)
78 {
79     switch (channel) {
80         case CS_RED:
81             return [CIVector vectorWithX:1.0 Y:0.0 Z:0.0 W:0.0];
82         case CS_GREEN:
83             return [CIVector vectorWithX:0.0 Y:1.0 Z:0.0 W:0.0];            
84         case CS_BLUE:
85             return [CIVector vectorWithX:0.0 Y:0.0 Z:1.0 W:0.0];
86         case CS_ALPHA:
87             return [CIVector vectorWithX:0.0 Y:0.0 Z:0.0 W:1.0];
88         default:
89             return [CIVector vectorWithX:0.0 Y:0.0 Z:0.0 W:0.0];
90     }
91 }
92
93 KCanvasFilterQuartz::KCanvasFilterQuartz() : m_filterCIContext(0), m_filterCGLayer(0)
94 {
95     m_imagesByName = HardRetainWithNSRelease([[NSMutableDictionary alloc] init]);
96 }
97
98 KCanvasFilterQuartz::~KCanvasFilterQuartz()
99 {
100     ASSERT(!m_filterCGLayer);
101     ASSERT(!m_filterCIContext);
102     HardRelease(m_imagesByName);
103 }
104
105 void KCanvasFilterQuartz::prepareFilter(const FloatRect &bbox)
106 {
107     if (bbox.isEmpty() || !KRenderingDeviceQuartz::filtersEnabled() || m_effects.isEmpty())
108         return;
109
110     CGContextRef cgContext = static_cast<KRenderingDeviceQuartz*>(renderingDevice())->currentCGContext();
111     
112     // get a CIContext, and CGLayer for drawing in.
113     bool useSoftware = ! KRenderingDeviceQuartz::hardwareRenderingEnabled();
114     NSDictionary *contextOptions = nil;
115     
116     if (useSoftware)
117         contextOptions = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], kCIContextUseSoftwareRenderer, nil];
118     
119     // Use of CGBegin/EndTransparencyLayer around this call causes over release
120     // of cgContext due to it being created on an autorelease pool, and released
121     // after CGEndTransparencyLayer. Create local pool to fix.
122     // <http://bugzilla.opendarwin.org/show_bug.cgi?id=8425>
123     // <http://bugzilla.opendarwin.org/show_bug.cgi?id=6947>
124     // <rdar://problem/4647735>
125     NSAutoreleasePool *filterContextPool = [[NSAutoreleasePool alloc] init];
126     m_filterCIContext = HardRetain([CIContext contextWithCGContext:cgContext options:contextOptions]);
127     [filterContextPool drain];
128     
129     m_filterCGLayer = [m_filterCIContext createCGLayerWithSize:CGRect(bbox).size info:NULL];
130     
131     KRenderingDeviceContext *filterContext = new KRenderingDeviceContextQuartz(CGLayerGetContext(m_filterCGLayer));
132     renderingDevice()->pushContext(filterContext);
133     
134     filterContext->concatCTM(AffineTransform().translate(-1.0f * bbox.x(), -1.0f * bbox.y()));
135 }
136
137 void KCanvasFilterQuartz::applyFilter(const FloatRect &bbox)
138 {
139     if (bbox.isEmpty() || !KRenderingDeviceQuartz::filtersEnabled() || m_effects.isEmpty())
140         return;
141
142     // restore the previous context, delete the filter context.
143     delete (renderingDevice()->popContext());
144
145     // actually apply the filter effects
146     CIImage *inputImage = [CIImage imageWithCGLayer:m_filterCGLayer];
147     NSArray *filterStack = getCIFilterStack(inputImage);
148     if ([filterStack count]) {
149         CIImage *outputImage = [[filterStack lastObject] valueForKey:@"outputImage"];
150         if (outputImage) {
151             CGRect filterRect = CGRect(filterBBoxForItemBBox(bbox));
152             CGRect translated = filterRect;
153             CGPoint bboxOrigin = CGRect(bbox).origin;
154             CGRect sourceRect = CGRectIntersection(translated,[outputImage extent]);
155             
156             CGPoint destOrigin = sourceRect.origin;
157             destOrigin.x += bboxOrigin.x;
158             destOrigin.y += bboxOrigin.y;
159             
160             [m_filterCIContext drawImage:outputImage atPoint:destOrigin fromRect:sourceRect];
161         }
162     }
163     
164     CGLayerRelease(m_filterCGLayer);
165     m_filterCGLayer = 0;
166
167     HardRelease(m_filterCIContext);
168     m_filterCIContext = 0;
169 }
170
171 NSArray *KCanvasFilterQuartz::getCIFilterStack(CIImage *inputImage)
172 {
173     NSMutableArray *filterEffects = [NSMutableArray array];
174
175     DeprecatedValueListIterator<KCanvasFilterEffect *> it = m_effects.begin();
176     DeprecatedValueListIterator<KCanvasFilterEffect *> end = m_effects.end();
177
178     setImageForName(inputImage, "SourceGraphic"); // input
179     for (;it != end; it++) {
180         CIFilter *filter = (*it)->getCIFilter(this);
181         if (filter)
182             [filterEffects addObject:filter];
183     }
184     [m_imagesByName removeAllObjects]; // clean up before next time.
185
186     return filterEffects;
187 }
188
189 CIImage *KCanvasFilterQuartz::imageForName(const DeprecatedString& name) const
190 {
191     return [m_imagesByName objectForKey:name.getNSString()];
192 }
193
194 void KCanvasFilterQuartz::setImageForName(CIImage *image, const DeprecatedString &name)
195 {
196     [m_imagesByName setValue:image forKey:name.getNSString()];
197 }
198
199 void KCanvasFilterQuartz::setOutputImage(const KCanvasFilterEffect *filterEffect, CIImage *output)
200 {
201     if (!filterEffect->result().isEmpty())
202         setImageForName(output, filterEffect->result());
203     setImageForName(output, KCPreviousFilterOutputName);
204 }
205
206 static inline CIImage *alphaImageForImage(CIImage *image)
207 {
208     CIFilter *onlyAlpha = [CIFilter filterWithName:@"CIColorMatrix"];
209     CGFloat zero[4] = {0, 0, 0, 0};
210     [onlyAlpha setDefaults];
211     [onlyAlpha setValue:image forKey:@"inputImage"];
212     [onlyAlpha setValue:[CIVector vectorWithValues:zero count:4] forKey:@"inputRVector"];
213     [onlyAlpha setValue:[CIVector vectorWithValues:zero count:4] forKey:@"inputGVector"];
214     [onlyAlpha setValue:[CIVector vectorWithValues:zero count:4] forKey:@"inputBVector"];
215     return [onlyAlpha valueForKey:@"outputImage"];
216 }
217
218 CIImage *KCanvasFilterQuartz::inputImage(const KCanvasFilterEffect *filterEffect)
219 {
220     if (filterEffect->in().isEmpty()) {
221         CIImage *inImage = imageForName(KCPreviousFilterOutputName);
222         if (!inImage)
223             inImage = imageForName("SourceGraphic");
224         return inImage;
225     } else if (filterEffect->in() == "SourceAlpha") {
226         CIImage *sourceAlpha = imageForName(filterEffect->in());
227         if (!sourceAlpha) {
228             CIImage *sourceGraphic = imageForName("SourceGraphic");
229             if (!sourceGraphic)
230                 return nil;
231             sourceAlpha = alphaImageForImage(sourceGraphic);
232             setImageForName(sourceAlpha, "SourceAlpha");
233         }
234         return sourceAlpha;
235     }
236
237     return imageForName(filterEffect->in());
238 }
239
240 #pragma mark -
241 #pragma mark Filter Elements
242
243 #define FE_QUARTZ_SETUP_INPUT(name) \
244     CIImage *inputImage = quartzFilter->inputImage(this); \
245     FE_QUARTZ_CHECK_INPUT(inputImage) \
246     CIFilter *filter; \
247     BEGIN_BLOCK_OBJC_EXCEPTIONS; \
248     filter = [CIFilter filterWithName:name]; \
249     [filter setDefaults]; \
250     [filter setValue:inputImage forKey:@"inputImage"];
251
252 #define FE_QUARTZ_CHECK_INPUT(input) \
253     if (!input) \
254         return nil;
255
256 #define FE_QUARTZ_OUTPUT_RETURN \
257     quartzFilter->setOutputImage(this, [filter valueForKey:@"outputImage"]); \
258     return filter; \
259     END_BLOCK_OBJC_EXCEPTIONS; \
260     return nil;
261
262 #define FE_QUARTZ_CROP_TO_RECT(rect) \
263     { \
264         CIFilter *crop = [CIFilter filterWithName:@"CICrop"]; \
265         [crop setDefaults]; \
266         [crop setValue:[filter valueForKey:@"outputImage"] forKey:@"inputImage"]; \
267         [crop setValue:[CIVector vectorWithX:rect.origin.x Y:rect.origin.y Z:rect.size.width W:rect.size.height] forKey:@"inputRectangle"]; \
268         filter = crop; \
269     }
270
271 CIFilter *KCanvasFEBlendQuartz::getCIFilter(KCanvasFilterQuartz *quartzFilter) const
272 {
273     CIFilter *filter = nil;
274     BEGIN_BLOCK_OBJC_EXCEPTIONS;
275
276     switch (blendMode()) {
277     case BM_NORMAL:
278         // FIXME: I think this is correct....
279         filter = [CIFilter filterWithName:@"CISourceOverCompositing"];
280         break;
281     case BM_MULTIPLY:
282         filter = [CIFilter filterWithName:@"CIMultiplyBlendMode"];
283         break;
284     case BM_SCREEN:
285         filter = [CIFilter filterWithName:@"CIScreenBlendMode"];
286         break;
287     case BM_DARKEN:
288         filter = [CIFilter filterWithName:@"CIDarkenBlendMode"];
289         break;
290     case BM_LIGHTEN:
291         filter = [CIFilter filterWithName:@"CILightenBlendMode"];
292         break;
293     default:
294         LOG_ERROR("Unhandled blend mode: %i", blendMode());
295         return nil;
296     }
297
298     [filter setDefaults];
299     CIImage *inputImage = quartzFilter->inputImage(this);
300     FE_QUARTZ_CHECK_INPUT(inputImage);
301     [filter setValue:inputImage forKey:@"inputImage"];
302     CIImage *backgroundImage = quartzFilter->imageForName(in2());
303     FE_QUARTZ_CHECK_INPUT(backgroundImage);
304     [filter setValue:backgroundImage forKey:@"inputBackgroundImage"];
305
306     FE_QUARTZ_OUTPUT_RETURN;
307 }
308
309 #define deg2rad(d) ((d * (2.0 * M_PI))/360.0)
310
311 #define CMValuesCheck(expected, type) \
312     if (values().count() != expected) { \
313         NSLog(@"Error, incorrect number of values in ColorMatrix for type \"%s\", expected: %i actual: %i, ignoring filter.  Values:", type, expected, values().count()); \
314         for (unsigned int x=0; x < values().count(); x++) fprintf(stderr, " %f", values()[x]); \
315         fprintf(stderr, "\n"); \
316         return nil; \
317     }
318
319 CIFilter *KCanvasFEColorMatrixQuartz::getCIFilter(KCanvasFilterQuartz *quartzFilter) const
320 {
321     CIFilter *filter = nil;
322     BEGIN_BLOCK_OBJC_EXCEPTIONS;
323     switch (type()) {
324     case CMT_MATRIX:
325     {
326         CMValuesCheck(20, "matrix");
327         filter = [CIFilter filterWithName:@"CIColorMatrix"];
328         [filter setDefaults];
329         DeprecatedValueList<float> v = values();
330         [filter setValue:[CIVector vectorWithX:v[0] Y:v[1] Z:v[2] W:v[3]] forKey:@"inputRVector"];
331         [filter setValue:[CIVector vectorWithX:v[5] Y:v[6] Z:v[7] W:v[8]] forKey:@"inputGVector"];
332         [filter setValue:[CIVector vectorWithX:v[10] Y:v[11] Z:v[12] W:v[13]] forKey:@"inputBVector"];
333         [filter setValue:[CIVector vectorWithX:v[15] Y:v[16] Z:v[17] W:v[18]] forKey:@"inputAVector"];
334         [filter setValue:[CIVector vectorWithX:v[4] Y:v[9] Z:v[14] W:v[19]] forKey:@"inputBiasVector"];
335         break;
336     }
337     case CMT_SATURATE:
338     {
339         CMValuesCheck(1, "saturate");
340         filter = [CIFilter filterWithName:@"CIColorControls"];
341         [filter setDefaults];
342         float saturation = values()[0];
343         if ((saturation < 0.0) || (saturation > 3.0))
344                 NSLog(@"WARNING: Saturation adjustment: %f outside supported range.");
345         [filter setValue:[NSNumber numberWithFloat:saturation] forKey:@"inputSaturation"];
346         break;
347     }
348     case CMT_HUE_ROTATE:
349     {
350         CMValuesCheck(1, "hueRotate");
351         filter = [CIFilter filterWithName:@"CIHueAdjust"];
352         [filter setDefaults];
353         float radians = deg2rad(values()[0]);
354         [filter setValue:[NSNumber numberWithFloat:radians] forKey:@"inputAngle"];
355         break;
356     }
357     case CMT_LUMINANCE_TO_ALPHA:
358     {
359         CMValuesCheck(0, "luminanceToAlpha");
360         // FIXME: I bet there is an easy filter to do this.
361         filter = [CIFilter filterWithName:@"CIColorMatrix"];
362         [filter setDefaults];
363         CGFloat zero[4] = {0, 0, 0, 0};
364         CGFloat alpha[4] = {0.2125, 0.7154, 0.0721, 0};
365         [filter setValue:[CIVector vectorWithValues:zero count:4] forKey:@"inputRVector"];
366         [filter setValue:[CIVector vectorWithValues:zero count:4] forKey:@"inputGVector"];
367         [filter setValue:[CIVector vectorWithValues:zero count:4] forKey:@"inputBVector"];
368         [filter setValue:[CIVector vectorWithValues:alpha count:4] forKey:@"inputAVector"];
369         [filter setValue:[CIVector vectorWithValues:zero count:4] forKey:@"inputBiasVector"];
370         break;
371     }
372     default:
373         LOG_ERROR("Unhandled ColorMatrix type: %i", type());
374         return nil;
375     }
376     CIImage *inputImage = quartzFilter->inputImage(this);
377     FE_QUARTZ_CHECK_INPUT(inputImage);
378     [filter setValue:inputImage forKey:@"inputImage"];
379
380     FE_QUARTZ_OUTPUT_RETURN;
381 }
382
383 static CIImage *genImageFromTable(const Vector<float>& table)
384 {
385     int length = table.size();
386     int nBytes = length*4*sizeof(float);
387     float *tableStore = (float *)malloc(nBytes);
388     NSData *bitmapData = [NSData dataWithBytesNoCopy:tableStore length:nBytes];
389     for (Vector<float>::const_iterator it = table.begin(); it != table.end(); it++) {
390         const float value = *it;
391         *tableStore++ = value;
392         *tableStore++ = value;
393         *tableStore++ = value;
394         *tableStore++ = value;
395     }
396     return [CIImage imageWithBitmapData:bitmapData bytesPerRow:nBytes size:CGSizeMake(length, 1) format:kCIFormatRGBAf colorSpace:nil];
397 }
398
399 static CIFilter *filterForComponentFunc(const KCComponentTransferFunction& func)
400 {
401     CIFilter *filter;
402     switch (func.type) {
403         case CT_IDENTITY:
404             filter = [CIFilter filterWithName:@"WKIdentityTransfer"];
405             break;
406         case CT_TABLE:
407             filter = [CIFilter filterWithName:@"WKTableTransferFilter"];
408             break;
409         case CT_DISCRETE:
410             filter = [CIFilter filterWithName:@"WKDiscreteTransferFilter"];
411             break;
412         case CT_LINEAR:
413             filter = [CIFilter filterWithName:@"WKLinearTransfer"];            
414             break;
415         case CT_GAMMA:
416             filter = [CIFilter filterWithName:@"WKGammaTransfer"];
417             break;
418         default:
419             NSLog(@"WARNING: Unknown function type for feComponentTransfer");
420             //and to prevent the entire svg from failing as a result
421             filter = [CIFilter filterWithName:@"WKIdentityTransfer"];
422             break;
423     }
424     return filter;
425 }
426
427 static void setParametersForComponentFunc(CIFilter *filter, const KCComponentTransferFunction& func, CIVector *channelSelector)
428 {
429     switch (func.type) {
430         case CT_TABLE:
431             [filter setValue:genImageFromTable(func.tableValues) forKey:@"inputTable"];
432             [filter setValue:channelSelector forKey:@"inputSelector"];
433             break;
434         case CT_DISCRETE:
435             [filter setValue:genImageFromTable(func.tableValues) forKey:@"inputTable"];
436             [filter setValue:channelSelector forKey:@"inputSelector"];
437             break;
438         case CT_LINEAR:
439             [filter setValue:[NSNumber numberWithFloat:func.slope] forKey:@"inputSlope"];
440             [filter setValue:[NSNumber numberWithFloat:func.intercept] forKey:@"inputIntercept"];          
441             break;
442         case CT_GAMMA:
443             [filter setValue:[NSNumber numberWithFloat:func.amplitude] forKey:@"inputAmplitude"];
444             [filter setValue:[NSNumber numberWithFloat:func.exponent] forKey:@"inputExponent"];
445             [filter setValue:[NSNumber numberWithFloat:func.offset] forKey:@"inputOffset"];
446             break;
447         default:
448             //identity has no args
449             break;
450     }
451 }
452
453 static CIFilter *getFilterForFunc(const KCComponentTransferFunction& func, CIImage *inputImage, CIVector *channelSelector) 
454 {
455     CIFilter *filter = filterForComponentFunc(func);
456     [filter setDefaults];
457     
458     setParametersForComponentFunc(filter, func, channelSelector);
459     [filter setValue:inputImage forKey:@"inputImage"];
460     return filter;
461 }
462
463 CIFilter *KCanvasFEComponentTransferQuartz::getFunctionFilter(KCChannelSelectorType channel, CIImage *inputImage) const
464 {
465     switch (channel) {
466         case CS_RED:
467             return [getFilterForFunc(redFunction(), inputImage, getVectorForChannel(channel)) valueForKey:@"outputImage"];
468         case CS_GREEN: 
469             return [getFilterForFunc(greenFunction(), inputImage, getVectorForChannel(channel)) valueForKey:@"outputImage"];
470         case CS_BLUE:
471             return [getFilterForFunc(blueFunction(), inputImage, getVectorForChannel(channel)) valueForKey:@"outputImage"];
472         case CS_ALPHA:
473             return [getFilterForFunc(alphaFunction(), inputImage, getVectorForChannel(channel)) valueForKey:@"outputImage"];
474         default:
475             return nil;
476     }
477     
478 }
479
480 CIFilter *KCanvasFEComponentTransferQuartz::getCIFilter(KCanvasFilterQuartz *quartzFilter) const
481 {
482     [WKComponentMergeFilter class];
483     [WKIdentityTransferFilter class];
484     [WKTableTransferFilter class];
485     [WKDiscreteTransferFilter class];
486     [WKLinearTransferFilter class];
487     [WKGammaTransferFilter class];
488     
489     CIFilter *filter = nil;
490     BEGIN_BLOCK_OBJC_EXCEPTIONS;
491     filter = [CIFilter filterWithName:@"WKComponentMerge"];
492     if (!filter)
493         return nil;
494     [filter setDefaults];
495     CIImage *inputImage = quartzFilter->inputImage(this);
496     FE_QUARTZ_CHECK_INPUT(inputImage);    
497     
498     [filter setValue:getFunctionFilter(CS_RED, inputImage) forKey:@"inputFuncR"];
499     [filter setValue:getFunctionFilter(CS_GREEN, inputImage) forKey:@"inputFuncG"];
500     [filter setValue:getFunctionFilter(CS_BLUE, inputImage) forKey:@"inputFuncB"];
501     [filter setValue:getFunctionFilter(CS_ALPHA, inputImage) forKey:@"inputFuncA"];
502     
503     FE_QUARTZ_OUTPUT_RETURN;
504     return nil;
505 }
506
507 CIFilter *KCanvasFECompositeQuartz::getCIFilter(KCanvasFilterQuartz *quartzFilter) const
508 {
509     CIFilter *filter = nil;
510     BEGIN_BLOCK_OBJC_EXCEPTIONS;
511
512     switch (operation()) {
513     case CO_OVER:
514         filter = [CIFilter filterWithName:@"CISourceOverCompositing"];
515         break;
516     case CO_IN:
517         filter = [CIFilter filterWithName:@"CISourceInCompositing"];
518         break;
519     case CO_OUT:
520         filter = [CIFilter filterWithName:@"CISourceOutCompositing"];
521         break;
522     case CO_ATOP:
523         filter = [CIFilter filterWithName:@"CISourceAtopCompositing"];
524         break;
525     case CO_XOR:
526         //FIXME: I'm not sure this is right...
527         filter = [CIFilter filterWithName:@"CIExclusionBlendMode"];
528         break;
529     case CO_ARITHMETIC:
530         [WKArithmeticFilter class];
531         filter = [CIFilter filterWithName:@"WKArithmeticFilter"];
532         break;
533     }
534     
535     [filter setDefaults];
536     CIImage *inputImage = quartzFilter->inputImage(this);
537     CIImage *backgroundImage = quartzFilter->imageForName(in2());
538     FE_QUARTZ_CHECK_INPUT(inputImage);
539     FE_QUARTZ_CHECK_INPUT(backgroundImage);
540     [filter setValue:inputImage forKey:@"inputImage"];
541     [filter setValue:backgroundImage forKey:@"inputBackgroundImage"];
542     //FIXME: this seems ugly
543     if (operation() == CO_ARITHMETIC) {
544         [filter setValue:[NSNumber numberWithFloat:k1()] forKey:@"inputK1"];
545         [filter setValue:[NSNumber numberWithFloat:k2()] forKey:@"inputK2"];
546         [filter setValue:[NSNumber numberWithFloat:k3()] forKey:@"inputK3"];
547         [filter setValue:[NSNumber numberWithFloat:k4()] forKey:@"inputK4"];
548     }
549     FE_QUARTZ_OUTPUT_RETURN;
550 }
551
552 CIFilter *KCanvasFEDisplacementMapQuartz::getCIFilter(KCanvasFilterQuartz *quartzFilter) const
553 {
554     CIFilter *filter = nil;
555     BEGIN_BLOCK_OBJC_EXCEPTIONS;
556     [WKDisplacementMapFilter class];
557     filter = [CIFilter filterWithName:@"WKDisplacementMapFilter"];    
558     [filter setDefaults];
559     CIImage *inputImage = quartzFilter->inputImage(this);
560     CIImage *displacementMap = quartzFilter->imageForName(in2());
561     FE_QUARTZ_CHECK_INPUT(inputImage);
562     FE_QUARTZ_CHECK_INPUT(displacementMap);
563     [filter setValue:inputImage forKey:@"inputImage"];
564     [filter setValue:displacementMap forKey:@"inputDisplacementMap"];
565     [filter setValue:getVectorForChannel(xChannelSelector()) forKey:@"inputXChannelSelector"];
566     [filter setValue:getVectorForChannel(yChannelSelector()) forKey:@"inputYChannelSelector"];
567     [filter setValue:[NSNumber numberWithFloat:scale()] forKey:@"inputScale"];
568     FE_QUARTZ_OUTPUT_RETURN;
569 }
570
571 static inline CIFilter *getPointLightVectors(CIFilter * normals, CIVector * lightPosition, float surfaceScale)
572 {
573     CIFilter *filter;
574     BEGIN_BLOCK_OBJC_EXCEPTIONS;
575     filter = [CIFilter filterWithName:@"WKPointLight"];
576     if (!filter)
577         return nil;
578     [filter setDefaults];
579     [filter setValue:[normals valueForKey:@"outputImage"] forKey:@"inputNormalMap"];
580     [filter setValue:lightPosition forKey:@"inputLightPosition"];    
581     [filter setValue:[NSNumber numberWithFloat:surfaceScale] forKey:@"inputSurfaceScale"];
582     return filter; 
583     END_BLOCK_OBJC_EXCEPTIONS;
584     return nil;
585 }
586
587 static CIFilter *getLightVectors(CIFilter * normals, const KCLightSource * light, float surfaceScale)
588 {
589     [WKDistantLightFilter class];
590     [WKPointLightFilter class];
591     [WKSpotLightFilter class];
592
593     CIFilter *filter = nil;    
594     BEGIN_BLOCK_OBJC_EXCEPTIONS;
595     
596     switch (light->type()) {
597     case LS_DISTANT:
598     {
599         const KCDistantLightSource *dlight = static_cast<const KCDistantLightSource *>(light);
600         
601         filter = [CIFilter filterWithName:@"WKDistantLight"];
602         if (!filter)
603             return nil;
604         [filter setDefaults];
605         
606         float azimuth = dlight->azimuth();
607         float elevation = dlight->elevation();
608         azimuth=deg2rad(azimuth);
609         elevation=deg2rad(elevation);
610         float Lx = cos(azimuth)*cos(elevation);
611         float Ly = sin(azimuth)*cos(elevation);
612         float Lz = sin(elevation);
613         
614         [filter setValue:[normals valueForKey:@"outputImage"] forKey:@"inputNormalMap"];
615         [filter setValue:[CIVector vectorWithX:Lx Y:Ly Z:Lz] forKey:@"inputLightDirection"];
616         return filter;
617     }
618     case LS_POINT:
619     {
620         const KCPointLightSource *plight = static_cast<const KCPointLightSource *>(light);
621         return getPointLightVectors(normals, [CIVector vectorWithX:plight->position().x() Y:plight->position().y() Z:plight->position().z()], surfaceScale);
622     }
623     case LS_SPOT:
624     {
625         const KCSpotLightSource *slight = static_cast<const KCSpotLightSource *>(light);
626         filter = [CIFilter filterWithName:@"WKSpotLight"];
627         if (!filter)
628             return nil;
629         
630         CIFilter * pointLightFilter = getPointLightVectors(normals, [CIVector vectorWithX:slight->position().x() Y:slight->position().y() Z:slight->position().z()], surfaceScale);
631         if (!pointLightFilter)
632             return nil;
633         [filter setDefaults];
634         
635         [filter setValue:[pointLightFilter valueForKey:@"outputImage"] forKey:@"inputLightVectors"];
636         [filter setValue:[CIVector vectorWithX:slight->direction().x() Y:slight->direction().y() Z:slight->direction().z()] forKey:@"inputLightDirection"];
637         [filter setValue:[NSNumber numberWithFloat:slight->specularExponent()] forKey:@"inputSpecularExponent"];
638         [filter setValue:[NSNumber numberWithFloat:deg2rad(slight->limitingConeAngle())] forKey:@"inputLimitingConeAngle"];
639         return filter;
640     }
641     }
642     END_BLOCK_OBJC_EXCEPTIONS;
643     return nil;
644 }
645
646 static CIFilter *getNormalMap(CIImage *bumpMap, float scale)
647 {
648     [WKNormalMapFilter class];
649     CIFilter *filter;
650     BEGIN_BLOCK_OBJC_EXCEPTIONS;
651     filter = [CIFilter filterWithName:@"WKNormalMap"];   
652     [filter setDefaults];
653     
654     [filter setValue:bumpMap forKey:@"inputImage"];  
655     [filter setValue:[NSNumber numberWithFloat:scale] forKey:@"inputSurfaceScale"];
656     return filter;
657     END_BLOCK_OBJC_EXCEPTIONS;
658     return nil;
659 }
660
661 CIFilter *KCanvasFEDiffuseLightingQuartz::getCIFilter(KCanvasFilterQuartz *quartzFilter) const
662 {
663     const KCLightSource *light = lightSource();
664     if (!light)
665         return nil;
666     
667     [WKDiffuseLightingFilter class];
668     
669     CIFilter *filter;
670     BEGIN_BLOCK_OBJC_EXCEPTIONS;
671     filter = [CIFilter filterWithName:@"WKDiffuseLighting"];
672     if (!filter)
673         return nil;
674     
675     [filter setDefaults];
676     CIImage *inputImage = quartzFilter->inputImage(this);
677     FE_QUARTZ_CHECK_INPUT(inputImage);
678     CIFilter *normals = getNormalMap(inputImage, surfaceScale());
679     if (!normals) 
680         return nil;
681     
682     CIFilter *lightVectors = getLightVectors(normals, light, surfaceScale());
683     if (!lightVectors) 
684         return nil;
685     
686     [filter setValue:[normals valueForKey:@"outputImage"] forKey:@"inputNormalMap"];
687     [filter setValue:[lightVectors valueForKey:@"outputImage"] forKey:@"inputLightVectors"];
688     [filter setValue:ciColor(lightingColor()) forKey:@"inputLightingColor"];
689     [filter setValue:[NSNumber numberWithFloat:surfaceScale()] forKey:@"inputSurfaceScale"];
690     [filter setValue:[NSNumber numberWithFloat:diffuseConstant()] forKey:@"inputDiffuseConstant"];
691     [filter setValue:[NSNumber numberWithFloat:kernelUnitLengthX()] forKey:@"inputKernelUnitLengthX"];
692     [filter setValue:[NSNumber numberWithFloat:kernelUnitLengthY()] forKey:@"inputKernelUnitLengthY"];
693     
694     FE_QUARTZ_OUTPUT_RETURN;
695 }
696
697 CIFilter *KCanvasFEFloodQuartz::getCIFilter(KCanvasFilterQuartz *quartzFilter) const
698 {
699     CIFilter *filter;
700     BEGIN_BLOCK_OBJC_EXCEPTIONS;
701     filter = [CIFilter filterWithName:@"CIConstantColorGenerator"];
702     [filter setDefaults];
703     CGColorRef color = cgColor(floodColor());
704     CGColorRef withAlpha = CGColorCreateCopyWithAlpha(color,CGColorGetAlpha(color) * floodOpacity());
705     CIColor *inputColor = [CIColor colorWithCGColor:withAlpha];
706     CGColorRelease(color);
707     CGColorRelease(withAlpha);
708     [filter setValue:inputColor forKey:@"inputColor"];
709     
710     CGRect cropRect = CGRectMake(-100,-100,1000,1000); // HACK
711     if (!subRegion().isEmpty())
712         cropRect = subRegion();
713     FE_QUARTZ_CROP_TO_RECT(cropRect);
714     
715     FE_QUARTZ_OUTPUT_RETURN;
716 }
717
718 CIFilter *KCanvasFEImageQuartz::getCIFilter(KCanvasFilterQuartz *quartzFilter) const
719 {
720     if (!cachedImage())
721         return nil;
722
723     CIFilter *filter;
724     BEGIN_BLOCK_OBJC_EXCEPTIONS;
725     // FIXME: This is only partially implemented (only supports images)
726     CIImage *ciImage = [CIImage imageWithCGImage:cachedImage()->image()->getCGImageRef()];
727     
728     // FIXME: There is probably a nicer way to perform both of these transforms.
729     filter = [CIFilter filterWithName:@"CIAffineTransform"];
730     [filter setDefaults];
731     [filter setValue:ciImage forKey:@"inputImage"];
732     
733     CGAffineTransform cgTransform = CGAffineTransformMake(1,0,0,-1,0,cachedImage()->image()->rect().bottom());
734     NSAffineTransform *nsTransform = [NSAffineTransform transform];
735     [nsTransform setTransformStruct:*((NSAffineTransformStruct *)&cgTransform)];
736     [filter setValue:nsTransform forKey:@"inputTransform"];
737     
738     if (!subRegion().isEmpty()) {
739         CIFilter *scaleImage = [CIFilter filterWithName:@"CIAffineTransform"];
740         [scaleImage setDefaults];
741         [scaleImage setValue:[filter valueForKey:@"outputImage"] forKey:@"inputImage"];
742         
743         cgTransform = CGAffineTransformMakeMapBetweenRects(CGRect(cachedImage()->image()->rect()), subRegion());
744         [nsTransform setTransformStruct:*((NSAffineTransformStruct *)&cgTransform)];
745         [scaleImage setValue:nsTransform forKey:@"inputTransform"];
746         filter = scaleImage;
747     }
748     
749     FE_QUARTZ_OUTPUT_RETURN;
750 }
751
752 CIFilter *KCanvasFEGaussianBlurQuartz::getCIFilter(KCanvasFilterQuartz *quartzFilter) const
753 {
754     FE_QUARTZ_SETUP_INPUT(@"CIGaussianPyramid");
755
756     float inputRadius = stdDeviationX();
757     if (inputRadius != stdDeviationY()) {
758         float inputAspectRatio = stdDeviationX()/stdDeviationY();
759         // FIXME: inputAspectRatio only support the range .5 to 2.0!
760         [filter setValue:[NSNumber numberWithFloat:inputAspectRatio] forKey:@"inputAspectRatio"];
761     }
762     [filter setValue:[NSNumber numberWithFloat:inputRadius] forKey:@"inputRadius"];
763
764     FE_QUARTZ_OUTPUT_RETURN;
765 }
766
767 CIFilter *KCanvasFEMergeQuartz::getCIFilter(KCanvasFilterQuartz *quartzFilter) const
768 {
769     CIFilter *filter = nil;
770     BEGIN_BLOCK_OBJC_EXCEPTIONS;
771     DeprecatedStringList inputs = mergeInputs();
772     DeprecatedValueListIterator<DeprecatedString> it = inputs.begin();
773     DeprecatedValueListIterator<DeprecatedString> end = inputs.end();
774
775     CIImage *previousOutput = quartzFilter->inputImage(this);
776     for (;it != end; it++) {
777         CIImage *inputImage = quartzFilter->imageForName(*it);
778     FE_QUARTZ_CHECK_INPUT(inputImage);
779     FE_QUARTZ_CHECK_INPUT(previousOutput);
780         filter = [CIFilter filterWithName:@"CISourceOverCompositing"];
781         [filter setDefaults];
782         [filter setValue:inputImage forKey:@"inputImage"];
783         [filter setValue:previousOutput forKey:@"inputBackgroundImage"];
784         previousOutput = [filter valueForKey:@"outputImage"];
785     }
786     FE_QUARTZ_OUTPUT_RETURN;
787 }
788
789 CIFilter *KCanvasFEOffsetQuartz::getCIFilter(KCanvasFilterQuartz *quartzFilter) const
790 {
791     FE_QUARTZ_SETUP_INPUT(@"CIAffineTransform");
792     NSAffineTransform *offsetTransform = [NSAffineTransform transform];
793     [offsetTransform translateXBy:dx() yBy:dy()];
794     [filter setValue:offsetTransform  forKey:@"inputTransform"];
795     FE_QUARTZ_OUTPUT_RETURN;
796 }
797
798 CIFilter *KCanvasFESpecularLightingQuartz::getCIFilter(KCanvasFilterQuartz *quartzFilter) const
799 {  
800     const KCLightSource *light = lightSource();
801     if(!light)
802         return nil;
803     
804     [WKSpecularLightingFilter class];  
805     
806     CIFilter *filter;
807     BEGIN_BLOCK_OBJC_EXCEPTIONS;
808     filter = [CIFilter filterWithName:@"WKSpecularLighting"];
809     [filter setDefaults];
810     CIImage *inputImage = quartzFilter->inputImage(this);
811     FE_QUARTZ_CHECK_INPUT(inputImage);
812     CIFilter *normals = getNormalMap(inputImage, surfaceScale());
813     if (!normals) 
814         return nil;
815     CIFilter *lightVectors = getLightVectors(normals, light, surfaceScale());
816     if (!lightVectors) 
817         return nil;
818     [filter setValue:[normals valueForKey:@"outputImage"] forKey:@"inputNormalMap"];
819     [filter setValue:[lightVectors valueForKey:@"outputImage"] forKey:@"inputLightVectors"];
820     [filter setValue:ciColor(lightingColor()) forKey:@"inputLightingColor"];
821     [filter setValue:[NSNumber numberWithFloat:surfaceScale()] forKey:@"inputSurfaceScale"];
822     [filter setValue:[NSNumber numberWithFloat:specularConstant()] forKey:@"inputSpecularConstant"];
823     [filter setValue:[NSNumber numberWithFloat:specularExponent()] forKey:@"inputSpecularExponent"];
824     [filter setValue:[NSNumber numberWithFloat:kernelUnitLengthX()] forKey:@"inputKernelUnitLengthX"];
825     [filter setValue:[NSNumber numberWithFloat:kernelUnitLengthY()] forKey:@"inputKernelUnitLengthY"];
826     
827     FE_QUARTZ_OUTPUT_RETURN;
828 }
829
830 CIFilter *KCanvasFETileQuartz::getCIFilter(KCanvasFilterQuartz *quartzFilter) const
831 {
832     FE_QUARTZ_SETUP_INPUT(@"CIAffineTile");
833     FE_QUARTZ_OUTPUT_RETURN;
834 }
835
836 }
837
838 #endif // SVG_SUPPORT