Merge pull request #10 from gunyarakun/fix-invalid-return
[cocotron.git] / Onyx2D / O2Surface.m
blob2faa4f44f83552dee1c908083520db75b03cd7a3
1 /*------------------------------------------------------------------------
2  *
3  * Derivative of the OpenVG 1.0.1 Reference Implementation
4  * -------------------------------------
5  *
6  * Copyright (c) 2007 The Khronos Group Inc.
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a
9  * copy of this software and /or associated documentation files
10  * (the "Materials "), to deal in the Materials without restriction,
11  * including without limitation the rights to use, copy, modify, merge,
12  * publish, distribute, sublicense, and/or sell copies of the Materials,
13  * and to permit persons to whom the Materials are furnished to do so,
14  * subject to the following conditions: 
15  *
16  * The above copyright notice and this permission notice shall be included 
17  * in all copies or substantial portions of the Materials. 
18  *
19  * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
23  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR
25  * THE USE OR OTHER DEALINGS IN THE MATERIALS.
26  *
27  *-------------------------------------------------------------------*/
29 #import <Onyx2D/O2Surface.h>
30 #import <Onyx2D/O2ColorSpace.h>
31 #import <Onyx2D/O2DataProvider.h>
33 @implementation O2Surface
35 #define RI_MAX_GAUSSIAN_STD_DEVIATION   128.0f
37 /*-------------------------------------------------------------------*//*!
38 * \brief        Converts from the current internal format to another.
39 * \param        
40 * \return       
41 * \note         
42 *//*-------------------------------------------------------------------*/
44         //From Section 3.4.2 of OpenVG 1.0.1 spec
45         //1: sRGB = gamma(lRGB)
46         //2: lRGB = invgamma(sRGB)
47         //3: lL = 0.2126 lR + 0.7152 lG + 0.0722 lB
48         //4: lRGB = lL
49         //5: sL = gamma(lL)
50         //6: lL = invgamma(sL)
51         //7: sRGB = sL
53         //Source/Dest lRGB sRGB   lL   sL 
54         //lRGB          —    1    3    3,5 
55         //sRGB          2    —    2,3  2,3,5 
56         //lL            4    4,1  —    5 
57         //sL            7,2  7    6    — 
59 #if 0
60 // Can't use 'gamma' ?
61 static O2Float dogamma(O2Float c)
62 {    
63         if( c <= 0.00304f )
64                 c *= 12.92f;
65         else
66                 c = 1.0556f * (O2Float)pow(c, 1.0f/2.4f) - 0.0556f;
67         return c;
70 static O2Float invgamma(O2Float c)
72         if( c <= 0.03928f )
73                 c /= 12.92f;
74         else
75                 c = (O2Float)pow((c + 0.0556f)/1.0556f, 2.4f);
76         return c;
79 #endif
80 static O2Float lRGBtoL(O2Float r, O2Float g, O2Float b)
82         return 0.2126f*r + 0.7152f*g + 0.0722f*b;
85 static void colorToBytesLittle(O2Float color,uint8_t *scanline){
86    union {
87     unsigned char bytes[4];
88     float         f;
89    } u;
90    
91    u.f=color;
92    
93 #ifdef __LITTLE_ENDIAN__   
94    scanline[0]=u.bytes[0];
95    scanline[1]=u.bytes[1];
96    scanline[2]=u.bytes[2];
97    scanline[3]=u.bytes[3];
98 #else
99    scanline[3]=u.bytes[0];
100    scanline[2]=u.bytes[1];
101    scanline[1]=u.bytes[2];
102    scanline[0]=u.bytes[3];
103 #endif
106 static void O2SurfaceWrite_argb32f_to_argb32fLittle(O2Surface *self,int x,int y,O2argb32f *span,int length){
107    uint8_t* scanline = self->_pixelBytes + y * self->_bytesPerRow;
108    int i;
109    
110    scanline+=x*16;
111    for(i=0;i<length;i++){
112     O2argb32f rgba=*span++;
114     colorToBytesLittle(rgba.r,scanline);
115     scanline+=4;
116     colorToBytesLittle(rgba.g,scanline);
117     scanline+=4;
118     colorToBytesLittle(rgba.b,scanline);
119     scanline+=4;
120     colorToBytesLittle(rgba.a,scanline);
121     scanline+=4;
122    }
125 static void colorToBytesBig(O2Float color,uint8_t *scanline){
126    union {
127     unsigned char bytes[4];
128     float         f;
129    } u;
130    
131    u.f=color;
132    
133 #ifdef __BIG_ENDIAN__   
134    scanline[0]=u.bytes[0];
135    scanline[1]=u.bytes[1];
136    scanline[2]=u.bytes[2];
137    scanline[3]=u.bytes[3];
138 #else
139    scanline[3]=u.bytes[0];
140    scanline[2]=u.bytes[1];
141    scanline[1]=u.bytes[2];
142    scanline[0]=u.bytes[3];
143 #endif
146 static void O2SurfaceWrite_argb32f_to_argb32fBig(O2Surface *self,int x,int y,O2argb32f *span,int length){
147    uint8_t* scanline = self->_pixelBytes + y * self->_bytesPerRow;
148    int i;
149    
150    scanline+=x*16;
151    for(i=0;i<length;i++){
152     O2argb32f rgba=*span++;
154     colorToBytesBig(rgba.r,scanline);
155     scanline+=4;
156     colorToBytesBig(rgba.g,scanline);
157     scanline+=4;
158     colorToBytesBig(rgba.b,scanline);
159     scanline+=4;
160     colorToBytesBig(rgba.a,scanline);
161     scanline+=4;
162    }
165 static unsigned char colorToNibble(O2Float c){
166         return RI_INT_MIN(RI_INT_MAX(RI_FLOOR_TO_INT(c * (O2Float)0xF + 0.5f), 0), 0xF);
169 static void O2SurfaceWrite_argb32f_to_GA88(O2Surface *self,int x,int y,O2argb32f *span,int length){
170    uint8_t* scanline = self->_pixelBytes + y * self->_bytesPerRow;
171    int i;
172    
173    scanline+=x*2;
174    for(i=0;i<length;i++){
175     O2argb32f rgba=*span++;
177     *scanline++=O2ByteFromFloat(rgba.r);
178     *scanline++=O2ByteFromFloat(rgba.a);
179    }
182 static void O2SurfaceWrite_argb32f_to_G8(O2Surface *self,int x,int y,O2argb32f *span,int length){
183    uint8_t* scanline = self->_pixelBytes + y * self->_bytesPerRow;
184    int i;
185    
186    scanline+=x;
187    for(i=0;i<length;i++){
188     O2argb32f rgba=*span++;
190     *scanline++=O2ByteFromFloat(lRGBtoL(rgba.r,rgba.g,rgba.b));
191    }
195 static void O2SurfaceWrite_argb32f_to_argb8u(O2Surface *self,int x,int y,O2argb32f *span,int length){
196    uint8_t* scanline = self->_pixelBytes + y * self->_bytesPerRow;
197    int i;
198    
199    scanline+=x*4;
200    for(i=0;i<length;i++){
201     O2argb32f rgba=*span++;
203     *scanline++=O2ByteFromFloat(rgba.r);
204     *scanline++=O2ByteFromFloat(rgba.g);
205     *scanline++=O2ByteFromFloat(rgba.b);
206     *scanline++=O2ByteFromFloat(rgba.a);
207    }
210 static void O2SurfaceWrite_argb8u_to_argb8u(O2Surface *self,int x,int y,O2argb8u *span,int length){
211    uint8_t* scanline = self->_pixelBytes + y * self->_bytesPerRow;
212    int i;
213    
214    scanline+=x*4;
215    for(i=0;i<length;i++){
216     O2argb8u rgba=*span++;
218     *scanline++=rgba.r;
219     *scanline++=rgba.g;
220     *scanline++=rgba.b;
221     *scanline++=rgba.a;
222    }
225 static void O2SurfaceWrite_argb32f_to_ABGR8888(O2Surface *self,int x,int y,O2argb32f *span,int length){
226    uint8_t* scanline = self->_pixelBytes + y * self->_bytesPerRow;
227    int i;
228    
229    scanline+=x*4;
230    for(i=0;i<length;i++){
231     O2argb32f rgba=*span++;
233     *scanline++=O2ByteFromFloat(rgba.a);
234     *scanline++=O2ByteFromFloat(rgba.b);
235     *scanline++=O2ByteFromFloat(rgba.g);
236     *scanline++=O2ByteFromFloat(rgba.r);
237    }
240 static void O2SurfaceWrite_argb8u_to_ABGR8888(O2Surface *self,int x,int y,O2argb8u *span,int length){
241    uint8_t* scanline = self->_pixelBytes + y * self->_bytesPerRow;
242    int i;
243    
244    scanline+=x*4;
245    for(i=0;i<length;i++){
246     O2argb8u rgba=*span++;
248     *scanline++=rgba.a;
249     *scanline++=rgba.b;
250     *scanline++=rgba.g;
251     *scanline++=rgba.r;
252    }
255 static void O2SurfaceWrite_argb8u_to_BGRA8888(O2Surface *self,int x,int y,O2argb8u *span,int length){
256    uint8_t* scanline = self->_pixelBytes + y * self->_bytesPerRow;
257    int i;
258    
259    scanline+=x*4;
260    for(i=0;i<length;i++){
261     O2argb8u rgba=*span++;
263     *scanline++=rgba.b;
264     *scanline++=rgba.g;
265     *scanline++=rgba.r;
266     *scanline++=rgba.a;
267    }
270 static void O2SurfaceWrite_argb32f_to_RGBA4444(O2Surface *self,int x,int y,O2argb32f *span,int length){
271    uint8_t* scanline = self->_pixelBytes + y * self->_bytesPerRow;
272    int i;
273    
274    scanline+=x*2;
275    for(i=0;i<length;i++){
276     O2argb32f rgba=*span++;
278     *scanline++=colorToNibble(rgba.r)<<4|colorToNibble(rgba.g);
279     *scanline++=colorToNibble(rgba.b)<<4|colorToNibble(rgba.a);
280    }
283 static void O2SurfaceWrite_argb32f_to_BARG4444(O2Surface *self,int x,int y,O2argb32f *span,int length){
284    uint8_t* scanline = self->_pixelBytes + y * self->_bytesPerRow;
285    int i;
286    
287    scanline+=x*2;
288    for(i=0;i<length;i++){
289     O2argb32f rgba=*span++;
291     *scanline++=colorToNibble(rgba.b)<<4|colorToNibble(rgba.a);
292     *scanline++=colorToNibble(rgba.r)<<4|colorToNibble(rgba.g);
293    }
296 static void O2SurfaceWrite_argb32f_to_RGBA2222(O2Surface *self,int x,int y,O2argb32f *span,int length){
297    uint8_t* scanline = self->_pixelBytes + y * self->_bytesPerRow;
298    int i;
299    
300    scanline+=x;
301    for(i=0;i<length;i++){
302     O2argb32f rgba=*span++;
304     *scanline++=colorToNibble(rgba.a)<<6|colorToNibble(rgba.a)<<6|colorToNibble(rgba.a)<<2|colorToNibble(rgba.b);
305    }
308 static void O2SurfaceWrite_argb32f_to_CMYK8888(O2Surface *self,int x,int y,O2argb32f *span,int length){
309    uint8_t* scanline = self->_pixelBytes + y * self->_bytesPerRow;
310    int i;
311    
312    scanline+=x*4;
313    for(i=0;i<length;i++){
314     O2argb32f rgba=*span++;
316     *scanline++=O2ByteFromFloat(1.0-rgba.r);
317     *scanline++=O2ByteFromFloat(1.0-rgba.g);
318     *scanline++=O2ByteFromFloat(1.0-rgba.b);
319     *scanline++=O2ByteFromFloat(0);
320    }
323 static void O2SurfaceWrite_argb32f_to_argb8u_to_ANY(O2Surface *self,int x,int y,O2argb32f *span,int length){
324    O2argb8u span8888[length];
325    int i;
326    
327    for(i=0;i<length;i++){
328     O2argb32f rgba=*span++;
330     span8888[i].r=O2ByteFromFloat(rgba.r);
331     span8888[i].g=O2ByteFromFloat(rgba.g);
332     span8888[i].b=O2ByteFromFloat(rgba.b);
333     span8888[i].a=O2ByteFromFloat(rgba.a);
334    }
335    self->_writeargb8u(self,x,y,span8888,length);
338 static BOOL initFunctionsForParameters(O2Surface *self,size_t bitsPerComponent,size_t bitsPerPixel,O2ColorSpaceRef colorSpace,O2BitmapInfo bitmapInfo){
339    self->_writeargb32f=O2SurfaceWrite_argb32f_to_argb8u_to_ANY;// default
340    
341    switch(bitsPerComponent){
342    
343     case 32:
344      switch(bitsPerPixel){
345       case 32:
346        break;
347       case 128:
348        switch(bitmapInfo&kO2BitmapByteOrderMask){
349         case kO2BitmapByteOrderDefault:
350         case kO2BitmapByteOrder16Little:
351         case kO2BitmapByteOrder32Little:
352          self->_writeargb32f=O2SurfaceWrite_argb32f_to_argb32fLittle;
353          return YES;
354          
355         case kO2BitmapByteOrder16Big:
356         case kO2BitmapByteOrder32Big:
357          self->_writeargb32f=O2SurfaceWrite_argb32f_to_argb32fBig;
358          return YES;
359        }
360      }
361      break;
362      
363     case  8:
364      switch(bitsPerPixel){
365      
366       case 8:
367        self->_writeargb32f=O2SurfaceWrite_argb32f_to_G8;
368        return YES;
370       case 16:
371        self->_writeargb32f=O2SurfaceWrite_argb32f_to_GA88;
372        return YES;
374       case 24:
375        break;
376        
377       case 32:
378        if([colorSpace type]==kO2ColorSpaceModelRGB){
380         switch(bitmapInfo&kO2BitmapAlphaInfoMask){
381          case kO2ImageAlphaNone:
382           break;
383           
384          case kO2ImageAlphaLast:
385          case kO2ImageAlphaPremultipliedLast:
386           switch(bitmapInfo&kO2BitmapByteOrderMask){
387            case kO2BitmapByteOrderDefault:
388            case kO2BitmapByteOrder16Little:
389            case kO2BitmapByteOrder32Little:
390             self->_writeargb32f=O2SurfaceWrite_argb32f_to_ABGR8888;
391             self->_writeargb8u=O2SurfaceWrite_argb8u_to_ABGR8888;
392             return YES;
394            case kO2BitmapByteOrder16Big:
395            case kO2BitmapByteOrder32Big:
396             self->_writeargb32f=O2SurfaceWrite_argb32f_to_argb8u;
397             self->_writeargb8u=O2SurfaceWrite_argb8u_to_argb8u;
398             return YES;
399           }
401           break;
402           
403          case kO2ImageAlphaPremultipliedFirst:
404           switch(bitmapInfo&kO2BitmapByteOrderMask){
405            case kO2BitmapByteOrderDefault:
406            case kO2BitmapByteOrder16Little:
407            case kO2BitmapByteOrder32Little:
408             self->_writeargb8u=O2SurfaceWrite_argb8u_to_BGRA8888;
409             return YES;
410           }
411           break;
412                     
413          case kO2ImageAlphaFirst:
414           break;
415           
416          case kO2ImageAlphaNoneSkipLast:
417           break;
418           
419          case kO2ImageAlphaNoneSkipFirst:
420           break;
421         }
422        }
423        else if([colorSpace type]==kO2ColorSpaceModelCMYK){
424         switch(bitmapInfo&kO2BitmapByteOrderMask){
425          case kO2BitmapByteOrderDefault:
426          case kO2BitmapByteOrder16Little:
427          case kO2BitmapByteOrder32Little:
428           break;
429          
430          case kO2BitmapByteOrder16Big:
431          case kO2BitmapByteOrder32Big:
432           self->_writeargb32f=O2SurfaceWrite_argb32f_to_CMYK8888;
433          return YES;
434         }
435        }
436        break;
437      }
438      break;
439      
440     case  4:
441      switch(bitsPerPixel){
442       case 4:
443        break;
444       case 12:
445        break;
446       case 16:
447        switch(bitmapInfo&kO2BitmapByteOrderMask){
448         case kO2BitmapByteOrderDefault:
449         case kO2BitmapByteOrder16Little:
450         case kO2BitmapByteOrder32Little:
451          self->_writeargb32f=O2SurfaceWrite_argb32f_to_BARG4444;
452          return YES;
453          
454         case kO2BitmapByteOrder16Big:
455         case kO2BitmapByteOrder32Big:
456          self->_writeargb32f=O2SurfaceWrite_argb32f_to_RGBA4444;
457          return YES;
458        }
459      }
460      break;
461      
462     case  2:
463      switch(bitsPerPixel){
464       case 2:
465        break;
466       case 6:
467        break;
468       case 8:
469        self->_writeargb32f=O2SurfaceWrite_argb32f_to_RGBA2222;
470        return YES;
471      }
472      break;
474     case  1:
475      switch(bitsPerPixel){
476       case 1:
477        //  self->_writeargb32f=O2SurfaceWriteSpan_largb32f_PRE_01;
478        return YES;
479        
480       case 3:
481        break;
482      }
483      break;
484    }
485    return NO;   
488 -initWithBytes:(void *)bytes width:(size_t)width height:(size_t)height bitsPerComponent:(size_t)bitsPerComponent bytesPerRow:(size_t)bytesPerRow colorSpace:(O2ColorSpaceRef)colorSpace bitmapInfo:(O2BitmapInfo)bitmapInfo {
489    O2DataProvider *provider;
490    int bitsPerPixel=32;
491    
492    if(bytes!=NULL){
493     provider=[[[O2DataProvider alloc] initWithBytes:bytes length:bytesPerRow*height] autorelease];
494     m_ownsData=NO;
495    }
496    else {
497     if(bytesPerRow>0 && bytesPerRow<(width*bitsPerPixel)/8){
498      NSLog(@"invalid bytes per row=%zu",bytesPerRow);
499      bytesPerRow=0;
500     }
501     
502     if(bytesPerRow==0)
503      bytesPerRow=(width*bitsPerPixel)/8;
504      
505     NSMutableData *data=[NSMutableData dataWithLength:bytesPerRow*height*sizeof(uint8_t)]; // this will also zero the bytes
506     provider=[O2DataProviderCreateWithCFData((CFDataRef)data) autorelease];
507         m_ownsData=YES;
508    }
509    
510     if([super initWithWidth:width height:height bitsPerComponent:bitsPerComponent bitsPerPixel:bitsPerPixel bytesPerRow:bytesPerRow colorSpace:colorSpace bitmapInfo:bitmapInfo decoder: NULL provider:provider decode:NULL interpolate:YES renderingIntent:kO2RenderingIntentDefault]==nil)
511     return nil;
512    
513    if([provider isDirectAccess])
514     _pixelBytes=(void *)[provider bytes];
516    if(!initFunctionsForParameters(self,bitsPerComponent,_bitsPerPixel,colorSpace,bitmapInfo))
517     NSLog(@"O2Surface -init error, return");
519    _clampExternalPixels=NO; // only set to yes if premultiplied
520    pthread_mutex_init(&_lock,NULL);
521    return self;
524 -(void)dealloc {
525     _pixelBytes=NULL; // if we own it, it's in the provider, if not, no release
526     pthread_mutex_destroy(&_lock);
527     
528     [super dealloc];
531 void O2SurfaceLock(O2Surface *surface) {
532    pthread_mutex_lock(&(surface->_lock));
535 void O2SurfaceUnlock(O2Surface *surface) {
536    pthread_mutex_unlock(&(surface->_lock));
539 -(void *)pixelBytes {
540    return _pixelBytes;
543 -(void)setWidth:(size_t)width height:(size_t)height reallocateOnlyIfRequired:(BOOL)roir {
545    if(!m_ownsData)
546     return;
548    _width=width;
549    _height=height;
550    _bytesPerRow=width*_bitsPerPixel/8;
551    
552    NSUInteger size=_bytesPerRow*height*sizeof(uint8_t);
553    NSUInteger allocateSize=[[_provider data] length];
554    
555    if((size>allocateSize) || (!roir && size!=allocateSize)){
556     [_provider release];
557     
558     NSMutableData *data=[NSMutableData dataWithLength:size];
559        _provider=O2DataProviderCreateWithCFData((CFDataRef)data);
560     _pixelBytes=[data mutableBytes];
561    }
564 void *O2SurfaceGetPixelBytes(O2Surface *surface) {
565   return surface->_pixelBytes;
568 size_t O2SurfaceGetWidth(O2Surface *surface) {
569   return surface->_width;
572 size_t O2SurfaceGetHeight(O2Surface *surface) {
573   return surface->_height;
576 size_t O2SurfaceGetBytesPerRow(O2Surface *surface) {
577    return surface->_bytesPerRow;
581 O2ImageRef O2SurfaceCreateImage(O2Surface *self) {
582    NSData           *data=[[NSData alloc] initWithBytes:self->_pixelBytes length:self->_bytesPerRow*self->_height];
583    O2DataProviderRef provider=O2DataProviderCreateWithCFData((CFDataRef)data);
584   
585   O2Image *result=O2ImageCreate(self->_width,self->_height,self->_bitsPerComponent,self->_bitsPerPixel,self->_bytesPerRow,self->_colorSpace,
586      self->_bitmapInfo,provider,self->_decode,self->_interpolate,self->_renderingIntent);
587   
588   O2DataProviderRelease(provider);
589   [data release];
590   
591   return result;
594 void O2SurfaceWriteSpan_argb8u_PRE(O2Surface *self,int x,int y,O2argb8u *span,int length) {   
595    if(length==0)
596     return;
597    
598    self->_writeargb8u(self,x,y,span,length);
601 void O2SurfaceWriteSpan_largb32f_PRE(O2Surface *self,int x,int y,O2argb32f *span,int length) {   
602    if(length==0)
603     return;
605    self->_writeargb32f(self,x,y,span,length);
609 /*-------------------------------------------------------------------*//*!
610 * \brief        Applies Gaussian blur filter.
611 * \param        
612 * \return       
613 * \note         
614 *//*-------------------------------------------------------------------*/
616 static O2argb32f gaussianReadPixel(int x, int y, int w, int h,O2argb32f *image)
618         if(x < 0 || x >= w || y < 0 || y >= h) {        //apply tiling mode
619          return O2argb32fInit(0,0,0,0);
620         }
621         else
622         {
623                 RI_ASSERT(x >= 0 && x < w && y >= 0 && y < h);
624                 return image[y*w+x];
625         }
628 typedef struct O2GaussianKernel {
629  int      refCount;
630  int      xSize;
631  int      xShift;
632  O2Float  xScale;
633  O2Float *xValues;
635  int      ySize;
636  int      yShift;
637  O2Float  yScale;
638  O2Float *yValues;
639 } O2GaussianKernel;
641 O2GaussianKernel *O2CreateGaussianKernelWithDeviation(O2Float stdDeviation){
642    O2GaussianKernel *kernel=NSZoneMalloc(NULL,sizeof(O2GaussianKernel));
643    
644    kernel->refCount=1;
645    
646    O2Float stdDeviationX=stdDeviation;
647    O2Float stdDeviationY=stdDeviation;
648    
649         RI_ASSERT(stdDeviationX > 0.0f && stdDeviationY > 0.0f);
650         RI_ASSERT(stdDeviationX <= RI_MAX_GAUSSIAN_STD_DEVIATION && stdDeviationY <= RI_MAX_GAUSSIAN_STD_DEVIATION);
651        
652         //find a size for the kernel
653         O2Float totalWeightX = stdDeviationX*(O2Float)sqrt(2.0f*M_PI);
654         O2Float totalWeightY = stdDeviationY*(O2Float)sqrt(2.0f*M_PI);
655         const O2Float tolerance = 0.99f;        //use a kernel that covers 99% of the total Gaussian support
657         O2Float expScaleX = -1.0f / (2.0f*stdDeviationX*stdDeviationX);
658         O2Float expScaleY = -1.0f / (2.0f*stdDeviationY*stdDeviationY);
660         int kernelWidth = 0;
661         O2Float e = 0.0f;
662         O2Float sumX = 1.0f;    //the weight of the middle entry counted already
663         do{
664                 kernelWidth++;
665                 e = (O2Float)exp((O2Float)(kernelWidth * kernelWidth) * expScaleX);
666                 sumX += e*2.0f; //count left&right lobes
667         }while(sumX < tolerance*totalWeightX);
669         int kernelHeight = 0;
670         e = 0.0f;
671         O2Float sumY = 1.0f;    //the weight of the middle entry counted already
672         do{
673                 kernelHeight++;
674                 e = (O2Float)exp((O2Float)(kernelHeight * kernelHeight) * expScaleY);
675                 sumY += e*2.0f; //count left&right lobes
676         }while(sumY < tolerance*totalWeightY);
678         //make a separable kernel
679     kernel->xSize=kernelWidth*2+1;
680     kernel->xValues=NSZoneMalloc(NULL,sizeof(O2Float)*kernel->xSize);
681     kernel->xShift = kernelWidth;
682     kernel->xScale = 0.0f;
683     int i;
684         for(i=0;i<kernel->xSize;i++){
685                 int x = i-kernel->xShift;
686                 kernel->xValues[i] = (O2Float)exp((O2Float)x*(O2Float)x * expScaleX);
687                 kernel->xScale += kernel->xValues[i];
688         }
689         kernel->xScale = 1.0f / kernel->xScale; //NOTE: using the mathematical definition of the scaling term doesn't work since we cut the filter support early for performance
691     kernel->ySize=kernelHeight*2+1;
692     kernel->yValues=NSZoneMalloc(NULL,sizeof(O2Float)*kernel->ySize);
693     kernel->yShift = kernelHeight;
694     kernel->yScale = 0.0f;
695         for(i=0;i<kernel->ySize;i++)
696         {
697                 int y = i-kernel->yShift;
698                 kernel->yValues[i] = (O2Float)exp((O2Float)y*(O2Float)y * expScaleY);
699                 kernel->yScale += kernel->yValues[i];
700         }
701         kernel->yScale = 1.0f / kernel->yScale; //NOTE: using the mathematical definition of the scaling term doesn't work since we cut the filter support early for performance
702     
703     return kernel;
706 O2GaussianKernelRef O2GaussianKernelRetain(O2GaussianKernelRef kernel) {
707    if(kernel!=NULL)
708     kernel->refCount++;
709     
710    return kernel;
713 void O2GaussianKernelRelease(O2GaussianKernelRef kernel) {
714    if(kernel!=NULL){
715     kernel->refCount--;
716     if(kernel->refCount<=0){
717      NSZoneFree(NULL,kernel->xValues);
718      NSZoneFree(NULL,kernel->yValues);
719      NSZoneFree(NULL,kernel);
720     }
721    }
724 static O2argb32f argbFromColor(O2ColorRef color){   
725    size_t    count=O2ColorGetNumberOfComponents(color);
726    const float *components=O2ColorGetComponents(color);
728    if(count==2)
729     return O2argb32fInit(components[0],components[0],components[0],components[1]);
730    if(count==4)
731     return O2argb32fInit(components[0],components[1],components[2],components[3]);
732     
733    return O2argb32fInit(1,0,0,1);
736 void O2SurfaceGaussianBlur(O2Surface *self,O2Image * src, O2GaussianKernel *kernel,O2ColorRef color){
737    O2argb32f argbColor=argbFromColor(color);
738    
739         //the area to be written is an intersection of source and destination image areas.
740         //lower-left corners of the images are aligned.
741         int w = RI_INT_MIN(self->_width, src->_width);
742         int h = RI_INT_MIN(self->_height, src->_height);
743         RI_ASSERT(w > 0 && h > 0);
744     
745         O2argb32f *tmp=NSZoneMalloc(NULL,src->_width*src->_height*sizeof(O2argb32f));
747         //copy source region to tmp and do conversion
748     int i,j;
749         for(j=0;j<src->_height;j++){
750      O2argb32f *tmpRow=tmp+j*src->_width;
751      int         i,width=src->_width;
752      O2argb32f *direct=O2Image_read_argb32f(src,0,j,tmpRow,width);
753      
754      if(direct!=NULL){
755       for(i=0;i<width;i++)
756        tmpRow[i]=direct[i];
757      }
758      for(i=0;i<width;i++){
759       tmpRow[i].a=argbColor.a*tmpRow[i].a;
760       tmpRow[i].r=argbColor.r*tmpRow[i].a;
761       tmpRow[i].g=argbColor.g*tmpRow[i].a;
762       tmpRow[i].b=argbColor.b*tmpRow[i].a;
763      }
764      
765         }
767         O2argb32f *tmp2=NSZoneMalloc(NULL,w*src->_height*sizeof(O2argb32f));
769         //horizontal pass
770         for(j=0;j<src->_height;j++){
771                 for(i=0;i<w;i++){
772                         O2argb32f sum=O2argb32fInit(0,0,0,0);
773             int ki;
774                         for(ki=0;ki<kernel->xSize;ki++){
775                                 int x = i+ki-kernel->xShift;
776                                 sum=O2argb32fAdd(sum, O2argb32fMultiplyByFloat(gaussianReadPixel(x, j, src->_width, src->_height, tmp),kernel->xValues[ki]));
777                         }
778                         tmp2[j*w+i] = O2argb32fMultiplyByFloat(sum, kernel->xScale);
779                 }
780         }
781         //vertical pass
782         for(j=0;j<h;j++){
783                 for(i=0;i<w;i++){
784                         O2argb32f sum=O2argb32fInit(0,0,0,0);
785             int kj;
786                         for(kj=0;kj<kernel->ySize;kj++){
787                                 int y = j+kj-kernel->yShift;
788                                 sum=O2argb32fAdd(sum,  O2argb32fMultiplyByFloat(gaussianReadPixel(i, y, w, src->_height, tmp2), kernel->yValues[kj]));
789                         }
790             sum=O2argb32fMultiplyByFloat(sum, kernel->yScale);
791                         O2SurfaceWriteSpan_largb32f_PRE(self,i, j, &sum,1);
792                 }
793         }
794     NSZoneFree(NULL,tmp);
795     NSZoneFree(NULL,tmp2);
798 @end