2 * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved.
3 * Copyright (C) 2006 Dave MacLachlan (dmaclach@mac.com)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
30 #import "KCanvasFilterQuartz.h"
32 #import "BlockExceptions.h"
33 #import "CachedImage.h"
34 #import "FoundationExtras.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>
57 static const char* const KCPreviousFilterOutputName = "__previousOutput__";
59 static inline CIColor *ciColor(const Color &c)
61 CGColorRef colorCG = cgColor(c);
62 CIColor *colorCI = [CIColor colorWithCGColor:colorCG];
63 CGColorRelease(colorCG);
67 static inline CIVector *ciVector(KCanvasPoint3F point)
69 return [CIVector vectorWithX:point.x() Y:point.y() Z:point.z()];
72 static inline CIVector *ciVector(FloatPoint point)
74 return [CIVector vectorWithX:point.x() Y:point.y()];
77 static inline CIVector *getVectorForChannel(KCChannelSelectorType channel)
81 return [CIVector vectorWithX:1.0 Y:0.0 Z:0.0 W:0.0];
83 return [CIVector vectorWithX:0.0 Y:1.0 Z:0.0 W:0.0];
85 return [CIVector vectorWithX:0.0 Y:0.0 Z:1.0 W:0.0];
87 return [CIVector vectorWithX:0.0 Y:0.0 Z:0.0 W:1.0];
89 return [CIVector vectorWithX:0.0 Y:0.0 Z:0.0 W:0.0];
93 KCanvasFilterQuartz::KCanvasFilterQuartz() : m_filterCIContext(0), m_filterCGLayer(0)
95 m_imagesByName = HardRetainWithNSRelease([[NSMutableDictionary alloc] init]);
98 KCanvasFilterQuartz::~KCanvasFilterQuartz()
100 ASSERT(!m_filterCGLayer);
101 ASSERT(!m_filterCIContext);
102 HardRelease(m_imagesByName);
105 void KCanvasFilterQuartz::prepareFilter(const FloatRect &bbox)
107 if (bbox.isEmpty() || !KRenderingDeviceQuartz::filtersEnabled() || m_effects.isEmpty())
110 CGContextRef cgContext = static_cast<KRenderingDeviceQuartz*>(renderingDevice())->currentCGContext();
112 // get a CIContext, and CGLayer for drawing in.
113 bool useSoftware = ! KRenderingDeviceQuartz::hardwareRenderingEnabled();
114 NSDictionary *contextOptions = nil;
117 contextOptions = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], kCIContextUseSoftwareRenderer, nil];
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];
129 m_filterCGLayer = [m_filterCIContext createCGLayerWithSize:CGRect(bbox).size info:NULL];
131 KRenderingDeviceContext *filterContext = new KRenderingDeviceContextQuartz(CGLayerGetContext(m_filterCGLayer));
132 renderingDevice()->pushContext(filterContext);
134 filterContext->concatCTM(AffineTransform().translate(-1.0f * bbox.x(), -1.0f * bbox.y()));
137 void KCanvasFilterQuartz::applyFilter(const FloatRect &bbox)
139 if (bbox.isEmpty() || !KRenderingDeviceQuartz::filtersEnabled() || m_effects.isEmpty())
142 // restore the previous context, delete the filter context.
143 delete (renderingDevice()->popContext());
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"];
151 CGRect filterRect = CGRect(filterBBoxForItemBBox(bbox));
152 CGRect translated = filterRect;
153 CGPoint bboxOrigin = CGRect(bbox).origin;
154 CGRect sourceRect = CGRectIntersection(translated,[outputImage extent]);
156 CGPoint destOrigin = sourceRect.origin;
157 destOrigin.x += bboxOrigin.x;
158 destOrigin.y += bboxOrigin.y;
160 [m_filterCIContext drawImage:outputImage atPoint:destOrigin fromRect:sourceRect];
164 CGLayerRelease(m_filterCGLayer);
167 HardRelease(m_filterCIContext);
168 m_filterCIContext = 0;
171 NSArray *KCanvasFilterQuartz::getCIFilterStack(CIImage *inputImage)
173 NSMutableArray *filterEffects = [NSMutableArray array];
175 DeprecatedValueListIterator<KCanvasFilterEffect *> it = m_effects.begin();
176 DeprecatedValueListIterator<KCanvasFilterEffect *> end = m_effects.end();
178 setImageForName(inputImage, "SourceGraphic"); // input
179 for (;it != end; it++) {
180 CIFilter *filter = (*it)->getCIFilter(this);
182 [filterEffects addObject:filter];
184 [m_imagesByName removeAllObjects]; // clean up before next time.
186 return filterEffects;
189 CIImage *KCanvasFilterQuartz::imageForName(const DeprecatedString& name) const
191 return [m_imagesByName objectForKey:name.getNSString()];
194 void KCanvasFilterQuartz::setImageForName(CIImage *image, const DeprecatedString &name)
196 [m_imagesByName setValue:image forKey:name.getNSString()];
199 void KCanvasFilterQuartz::setOutputImage(const KCanvasFilterEffect *filterEffect, CIImage *output)
201 if (!filterEffect->result().isEmpty())
202 setImageForName(output, filterEffect->result());
203 setImageForName(output, KCPreviousFilterOutputName);
206 static inline CIImage *alphaImageForImage(CIImage *image)
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"];
218 CIImage *KCanvasFilterQuartz::inputImage(const KCanvasFilterEffect *filterEffect)
220 if (filterEffect->in().isEmpty()) {
221 CIImage *inImage = imageForName(KCPreviousFilterOutputName);
223 inImage = imageForName("SourceGraphic");
225 } else if (filterEffect->in() == "SourceAlpha") {
226 CIImage *sourceAlpha = imageForName(filterEffect->in());
228 CIImage *sourceGraphic = imageForName("SourceGraphic");
231 sourceAlpha = alphaImageForImage(sourceGraphic);
232 setImageForName(sourceAlpha, "SourceAlpha");
237 return imageForName(filterEffect->in());
241 #pragma mark Filter Elements
243 #define FE_QUARTZ_SETUP_INPUT(name) \
244 CIImage *inputImage = quartzFilter->inputImage(this); \
245 FE_QUARTZ_CHECK_INPUT(inputImage) \
247 BEGIN_BLOCK_OBJC_EXCEPTIONS; \
248 filter = [CIFilter filterWithName:name]; \
249 [filter setDefaults]; \
250 [filter setValue:inputImage forKey:@"inputImage"];
252 #define FE_QUARTZ_CHECK_INPUT(input) \
256 #define FE_QUARTZ_OUTPUT_RETURN \
257 quartzFilter->setOutputImage(this, [filter valueForKey:@"outputImage"]); \
259 END_BLOCK_OBJC_EXCEPTIONS; \
262 #define FE_QUARTZ_CROP_TO_RECT(rect) \
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"]; \
271 CIFilter *KCanvasFEBlendQuartz::getCIFilter(KCanvasFilterQuartz *quartzFilter) const
273 CIFilter *filter = nil;
274 BEGIN_BLOCK_OBJC_EXCEPTIONS;
276 switch (blendMode()) {
278 // FIXME: I think this is correct....
279 filter = [CIFilter filterWithName:@"CISourceOverCompositing"];
282 filter = [CIFilter filterWithName:@"CIMultiplyBlendMode"];
285 filter = [CIFilter filterWithName:@"CIScreenBlendMode"];
288 filter = [CIFilter filterWithName:@"CIDarkenBlendMode"];
291 filter = [CIFilter filterWithName:@"CILightenBlendMode"];
294 LOG_ERROR("Unhandled blend mode: %i", blendMode());
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"];
306 FE_QUARTZ_OUTPUT_RETURN;
309 #define deg2rad(d) ((d * (2.0 * M_PI))/360.0)
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"); \
319 CIFilter *KCanvasFEColorMatrixQuartz::getCIFilter(KCanvasFilterQuartz *quartzFilter) const
321 CIFilter *filter = nil;
322 BEGIN_BLOCK_OBJC_EXCEPTIONS;
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"];
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"];
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"];
357 case CMT_LUMINANCE_TO_ALPHA:
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"];
373 LOG_ERROR("Unhandled ColorMatrix type: %i", type());
376 CIImage *inputImage = quartzFilter->inputImage(this);
377 FE_QUARTZ_CHECK_INPUT(inputImage);
378 [filter setValue:inputImage forKey:@"inputImage"];
380 FE_QUARTZ_OUTPUT_RETURN;
383 static CIImage *genImageFromTable(const Vector<float>& table)
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;
396 return [CIImage imageWithBitmapData:bitmapData bytesPerRow:nBytes size:CGSizeMake(length, 1) format:kCIFormatRGBAf colorSpace:nil];
399 static CIFilter *filterForComponentFunc(const KCComponentTransferFunction& func)
404 filter = [CIFilter filterWithName:@"WKIdentityTransfer"];
407 filter = [CIFilter filterWithName:@"WKTableTransferFilter"];
410 filter = [CIFilter filterWithName:@"WKDiscreteTransferFilter"];
413 filter = [CIFilter filterWithName:@"WKLinearTransfer"];
416 filter = [CIFilter filterWithName:@"WKGammaTransfer"];
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"];
427 static void setParametersForComponentFunc(CIFilter *filter, const KCComponentTransferFunction& func, CIVector *channelSelector)
431 [filter setValue:genImageFromTable(func.tableValues) forKey:@"inputTable"];
432 [filter setValue:channelSelector forKey:@"inputSelector"];
435 [filter setValue:genImageFromTable(func.tableValues) forKey:@"inputTable"];
436 [filter setValue:channelSelector forKey:@"inputSelector"];
439 [filter setValue:[NSNumber numberWithFloat:func.slope] forKey:@"inputSlope"];
440 [filter setValue:[NSNumber numberWithFloat:func.intercept] forKey:@"inputIntercept"];
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"];
448 //identity has no args
453 static CIFilter *getFilterForFunc(const KCComponentTransferFunction& func, CIImage *inputImage, CIVector *channelSelector)
455 CIFilter *filter = filterForComponentFunc(func);
456 [filter setDefaults];
458 setParametersForComponentFunc(filter, func, channelSelector);
459 [filter setValue:inputImage forKey:@"inputImage"];
463 CIFilter *KCanvasFEComponentTransferQuartz::getFunctionFilter(KCChannelSelectorType channel, CIImage *inputImage) const
467 return [getFilterForFunc(redFunction(), inputImage, getVectorForChannel(channel)) valueForKey:@"outputImage"];
469 return [getFilterForFunc(greenFunction(), inputImage, getVectorForChannel(channel)) valueForKey:@"outputImage"];
471 return [getFilterForFunc(blueFunction(), inputImage, getVectorForChannel(channel)) valueForKey:@"outputImage"];
473 return [getFilterForFunc(alphaFunction(), inputImage, getVectorForChannel(channel)) valueForKey:@"outputImage"];
480 CIFilter *KCanvasFEComponentTransferQuartz::getCIFilter(KCanvasFilterQuartz *quartzFilter) const
482 [WKComponentMergeFilter class];
483 [WKIdentityTransferFilter class];
484 [WKTableTransferFilter class];
485 [WKDiscreteTransferFilter class];
486 [WKLinearTransferFilter class];
487 [WKGammaTransferFilter class];
489 CIFilter *filter = nil;
490 BEGIN_BLOCK_OBJC_EXCEPTIONS;
491 filter = [CIFilter filterWithName:@"WKComponentMerge"];
494 [filter setDefaults];
495 CIImage *inputImage = quartzFilter->inputImage(this);
496 FE_QUARTZ_CHECK_INPUT(inputImage);
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"];
503 FE_QUARTZ_OUTPUT_RETURN;
507 CIFilter *KCanvasFECompositeQuartz::getCIFilter(KCanvasFilterQuartz *quartzFilter) const
509 CIFilter *filter = nil;
510 BEGIN_BLOCK_OBJC_EXCEPTIONS;
512 switch (operation()) {
514 filter = [CIFilter filterWithName:@"CISourceOverCompositing"];
517 filter = [CIFilter filterWithName:@"CISourceInCompositing"];
520 filter = [CIFilter filterWithName:@"CISourceOutCompositing"];
523 filter = [CIFilter filterWithName:@"CISourceAtopCompositing"];
526 //FIXME: I'm not sure this is right...
527 filter = [CIFilter filterWithName:@"CIExclusionBlendMode"];
530 [WKArithmeticFilter class];
531 filter = [CIFilter filterWithName:@"WKArithmeticFilter"];
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"];
549 FE_QUARTZ_OUTPUT_RETURN;
552 CIFilter *KCanvasFEDisplacementMapQuartz::getCIFilter(KCanvasFilterQuartz *quartzFilter) const
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;
571 static inline CIFilter *getPointLightVectors(CIFilter * normals, CIVector * lightPosition, float surfaceScale)
574 BEGIN_BLOCK_OBJC_EXCEPTIONS;
575 filter = [CIFilter filterWithName:@"WKPointLight"];
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"];
583 END_BLOCK_OBJC_EXCEPTIONS;
587 static CIFilter *getLightVectors(CIFilter * normals, const KCLightSource * light, float surfaceScale)
589 [WKDistantLightFilter class];
590 [WKPointLightFilter class];
591 [WKSpotLightFilter class];
593 CIFilter *filter = nil;
594 BEGIN_BLOCK_OBJC_EXCEPTIONS;
596 switch (light->type()) {
599 const KCDistantLightSource *dlight = static_cast<const KCDistantLightSource *>(light);
601 filter = [CIFilter filterWithName:@"WKDistantLight"];
604 [filter setDefaults];
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);
614 [filter setValue:[normals valueForKey:@"outputImage"] forKey:@"inputNormalMap"];
615 [filter setValue:[CIVector vectorWithX:Lx Y:Ly Z:Lz] forKey:@"inputLightDirection"];
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);
625 const KCSpotLightSource *slight = static_cast<const KCSpotLightSource *>(light);
626 filter = [CIFilter filterWithName:@"WKSpotLight"];
630 CIFilter * pointLightFilter = getPointLightVectors(normals, [CIVector vectorWithX:slight->position().x() Y:slight->position().y() Z:slight->position().z()], surfaceScale);
631 if (!pointLightFilter)
633 [filter setDefaults];
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"];
642 END_BLOCK_OBJC_EXCEPTIONS;
646 static CIFilter *getNormalMap(CIImage *bumpMap, float scale)
648 [WKNormalMapFilter class];
650 BEGIN_BLOCK_OBJC_EXCEPTIONS;
651 filter = [CIFilter filterWithName:@"WKNormalMap"];
652 [filter setDefaults];
654 [filter setValue:bumpMap forKey:@"inputImage"];
655 [filter setValue:[NSNumber numberWithFloat:scale] forKey:@"inputSurfaceScale"];
657 END_BLOCK_OBJC_EXCEPTIONS;
661 CIFilter *KCanvasFEDiffuseLightingQuartz::getCIFilter(KCanvasFilterQuartz *quartzFilter) const
663 const KCLightSource *light = lightSource();
667 [WKDiffuseLightingFilter class];
670 BEGIN_BLOCK_OBJC_EXCEPTIONS;
671 filter = [CIFilter filterWithName:@"WKDiffuseLighting"];
675 [filter setDefaults];
676 CIImage *inputImage = quartzFilter->inputImage(this);
677 FE_QUARTZ_CHECK_INPUT(inputImage);
678 CIFilter *normals = getNormalMap(inputImage, surfaceScale());
682 CIFilter *lightVectors = getLightVectors(normals, light, surfaceScale());
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"];
694 FE_QUARTZ_OUTPUT_RETURN;
697 CIFilter *KCanvasFEFloodQuartz::getCIFilter(KCanvasFilterQuartz *quartzFilter) const
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"];
710 CGRect cropRect = CGRectMake(-100,-100,1000,1000); // HACK
711 if (!subRegion().isEmpty())
712 cropRect = subRegion();
713 FE_QUARTZ_CROP_TO_RECT(cropRect);
715 FE_QUARTZ_OUTPUT_RETURN;
718 CIFilter *KCanvasFEImageQuartz::getCIFilter(KCanvasFilterQuartz *quartzFilter) const
724 BEGIN_BLOCK_OBJC_EXCEPTIONS;
725 // FIXME: This is only partially implemented (only supports images)
726 CIImage *ciImage = [CIImage imageWithCGImage:cachedImage()->image()->getCGImageRef()];
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"];
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"];
738 if (!subRegion().isEmpty()) {
739 CIFilter *scaleImage = [CIFilter filterWithName:@"CIAffineTransform"];
740 [scaleImage setDefaults];
741 [scaleImage setValue:[filter valueForKey:@"outputImage"] forKey:@"inputImage"];
743 cgTransform = CGAffineTransformMakeMapBetweenRects(CGRect(cachedImage()->image()->rect()), subRegion());
744 [nsTransform setTransformStruct:*((NSAffineTransformStruct *)&cgTransform)];
745 [scaleImage setValue:nsTransform forKey:@"inputTransform"];
749 FE_QUARTZ_OUTPUT_RETURN;
752 CIFilter *KCanvasFEGaussianBlurQuartz::getCIFilter(KCanvasFilterQuartz *quartzFilter) const
754 FE_QUARTZ_SETUP_INPUT(@"CIGaussianPyramid");
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"];
762 [filter setValue:[NSNumber numberWithFloat:inputRadius] forKey:@"inputRadius"];
764 FE_QUARTZ_OUTPUT_RETURN;
767 CIFilter *KCanvasFEMergeQuartz::getCIFilter(KCanvasFilterQuartz *quartzFilter) const
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();
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"];
786 FE_QUARTZ_OUTPUT_RETURN;
789 CIFilter *KCanvasFEOffsetQuartz::getCIFilter(KCanvasFilterQuartz *quartzFilter) const
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;
798 CIFilter *KCanvasFESpecularLightingQuartz::getCIFilter(KCanvasFilterQuartz *quartzFilter) const
800 const KCLightSource *light = lightSource();
804 [WKSpecularLightingFilter class];
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());
815 CIFilter *lightVectors = getLightVectors(normals, light, surfaceScale());
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"];
827 FE_QUARTZ_OUTPUT_RETURN;
830 CIFilter *KCanvasFETileQuartz::getCIFilter(KCanvasFilterQuartz *quartzFilter) const
832 FE_QUARTZ_SETUP_INPUT(@"CIAffineTile");
833 FE_QUARTZ_OUTPUT_RETURN;
838 #endif // SVG_SUPPORT