tizen 2.3.1 release
[framework/graphics/cairo.git] / src / cairo-quartz-filters.c
1 /* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
2 /* cairo - a vector graphics library with display and print output
3  *
4  * Copyright � 2013 Samsung Research America - Silicon Valley
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it either under the terms of the GNU Lesser General Public
8  * License version 2.1 as published by the Free Software Foundation
9  * (the "LGPL") or, at your option, under the terms of the Mozilla
10  * Public License Version 1.1 (the "MPL"). If you do not alter this
11  * notice, a recipient may use your version of this file under either
12  * the MPL or the LGPL.
13  *
14  * You should have received a copy of the LGPL along with this library
15  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
17  * You should have received a copy of the MPL along with this library
18  * in the file COPYING-MPL-1.1
19  *
20  * The contents of this file are subject to the Mozilla Public License
21  * Version 1.1 (the "License"); you may not use this file except in
22  * compliance with the License. You may obtain a copy of the License at
23  * http://www.mozilla.org/MPL/
24  *
25  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
26  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
27  * the specific language governing rights and limitations.
28  *
29  * The Original Code is the cairo graphics library.
30  *
31  * The Initial Developer of the Original Code is Mozilla Foundation.
32  *
33  * Contributor(s):
34  *      Henry Song <henry.song@samsung.com>
35  */
36
37 #define _GNU_SOURCE /* required for RTLD_DEFAULT */
38 #include "cairoint.h"
39 #include "cairo-pattern-private.h"
40 #include "cairo-quartz-private.h"
41 #include "cairo-quartz.h"
42 #include <Accelerate/Accelerate.h>
43
44 #define CAIRO_QUARTZ_MAX_SCALE 4
45
46 static int16_t *
47 _cairo_quartz_pattern_create_gaussian_matrix (const cairo_pattern_t *pattern,
48                                               int *row, int *col,
49                                               int *sum,
50                                               int *shrink_x, int *shrink_y)
51 {
52     double x_sigma, y_sigma;
53     double x_sigma_sq, y_sigma_sq;
54     int n;
55     double *buffer;
56     int16_t *i_buffer;
57     int i, x, y;
58     double u, v;
59     double u1, v1;
60     int x_radius, y_radius;
61     int i_row, i_col;
62     int x_factor, y_factor;
63     cairo_rectangle_int_t extents;
64     int width, height;
65     int max_factor;
66     double max_sigma;
67
68     max_factor = CAIRO_QUARTZ_MAX_SCALE;;
69     max_sigma = CAIRO_MAX_SIGMA;
70
71     width = CAIRO_MIN_SHRINK_SIZE;
72     height = CAIRO_MIN_SHRINK_SIZE;
73
74     if (_cairo_surface_get_extents (((cairo_surface_pattern_t *)pattern)->surface, &extents)) {
75         width = extents.width;
76         height = extents.height;
77     }
78
79     x_factor = y_factor = 1;
80     x_sigma = pattern->x_sigma;
81     y_sigma = pattern->y_sigma;
82
83     /* no blur */
84     if (x_sigma == 0.0 && y_sigma == 0.0) {
85         return NULL;
86     }
87
88     if (x_sigma == 0.0)
89         x_radius = 0;
90     else {
91         while (x_sigma >= max_sigma) {
92             if (width <= CAIRO_MIN_SHRINK_SIZE || x_factor >= max_factor)
93                 break;
94
95             x_sigma *= 0.5;
96             x_factor *= 2;
97             width *= 0.5;
98         }
99     }
100
101     if (y_sigma == 0.0)
102         y_radius = 0;
103     else {
104         while (y_sigma >= max_sigma) {
105             if (height <= CAIRO_MIN_SHRINK_SIZE || y_factor >= max_factor)
106                 break;
107
108             y_sigma *= 0.5;
109             y_factor *= 2;
110             height *= 0.5;
111         }
112     }
113
114     /* 2D gaussian
115      * f(x, y) = exp (-((x-x0)^2/(2*x_sigma^2)+(y-y0)^2/(2*y_sigma*2)))
116      */
117     x_radius = x_sigma * 2;
118     y_radius = y_sigma * 2;
119
120     i_row = y_radius;
121     i_col = x_radius;
122     n = (2 * i_row + 1) * (2 * i_col + 1);
123
124     x_sigma_sq = 2 * x_sigma * x_sigma;
125     y_sigma_sq = 2 * y_sigma * y_sigma;
126
127     buffer = _cairo_malloc_ab (n, sizeof (double));
128     if (! buffer)
129         return NULL;
130     i_buffer = _cairo_malloc_ab (n, sizeof (i_buffer));
131     if (! i_buffer) {
132         free (buffer);
133         return NULL;
134     }
135     i = 0;
136     *sum = 0;
137
138     for (y = -i_row; y <= i_row; y++) {
139         for (x = -i_col; x <= i_col; x++) {
140             u = x * x;
141             v = y * y;
142             if (u == 0.0)
143                 u1 = 0.0;
144             else
145                 u1 = u / x_sigma_sq;
146
147             if (v == 0.0)
148                 v1 = 0.0;
149             else
150                 v1 = v / y_sigma_sq;
151             buffer[i] = exp (-(u1 + v1));
152             i_buffer[i] = ceil (buffer[i] - 0.5);
153             *sum += i_buffer[i];
154             i++;
155         }
156     }
157
158     free (buffer);
159
160     *row = i_row * 2 + 1;
161     *col = i_col * 2 + 1;
162     *shrink_x = x_factor;
163     *shrink_y = y_factor;
164
165     return i_buffer;
166 }
167
168 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
169 static CGContextRef
170 _cairo_quartz_get_image_context (CGImageRef image)
171 {
172     int width, height;
173     int bytes_per_row;
174     CGContextRef context;
175     CGColorSpaceRef color_space;
176     CGBitmapInfo bitmap_info;
177     CGRect size;
178
179     void *buffer;
180
181     if (image == NULL)
182         return NULL;
183
184     width = CGImageGetWidth (image);
185     height = CGImageGetHeight (image);
186     bytes_per_row = CGImageGetBytesPerRow (image);
187
188     color_space = CGImageGetColorSpace (image);
189     buffer = malloc (sizeof (char) * bytes_per_row * height);
190     if (! buffer)
191         return NULL;
192
193     bitmap_info = CGImageGetBitmapInfo (image);
194
195     /* create output image bitmap context */
196     context = CGBitmapContextCreate (buffer, width, height,
197                                      CGImageGetBitsPerComponent (image),
198                                      bytes_per_row,
199                                      color_space,
200                                      bitmap_info);
201
202     if (! context) {
203         free (buffer);
204         return NULL;
205     }
206
207     size = CGRectMake (0, 0, width, height);
208
209     CGContextDrawImage (context, size, image);
210
211     return context;
212 }
213 #endif
214
215 static cairo_int_status_t
216 _cairo_quartz_resize_image (CGImageRef src, double x_resize_factor,
217                             double y_resize_factor, CGImageRef *out)
218 {
219     int width, height;
220     int bytes_per_row;
221     int bytes_per_pixel;
222     CGContextRef out_bitmap_context;
223     CGColorSpaceRef color_space;
224     CGBitmapInfo bitmap_info;
225     CGRect size;
226
227     void *buffer;
228
229     if (src == NULL)
230         return CAIRO_INT_STATUS_UNSUPPORTED;
231
232     if (x_resize_factor <= 0.0 ||
233         y_resize_factor <= 0.0)
234         return CAIRO_INT_STATUS_UNSUPPORTED;
235
236     width = CGImageGetWidth (src) * x_resize_factor;
237     height = CGImageGetHeight (src) * y_resize_factor;
238     bytes_per_pixel = CGImageGetBytesPerRow (src) / CGImageGetWidth (src);
239
240     color_space = CGImageGetColorSpace (src);
241     bytes_per_row = bytes_per_pixel * width;
242     buffer = malloc (sizeof (char) * bytes_per_row * height);
243     if (! buffer)
244         return CAIRO_INT_STATUS_NO_MEMORY;
245
246     bitmap_info = CGImageGetBitmapInfo (src);
247
248     /* create output image bitmap context */
249     out_bitmap_context = CGBitmapContextCreate (buffer, width, height,
250                                                 CGImageGetBitsPerComponent (src),
251                                                 bytes_per_row,
252                                                 color_space,
253                                                 bitmap_info);
254
255     size = CGRectMake (0, 0, width, height);
256
257     CGContextDrawImage (out_bitmap_context, size, src);
258
259     *out = CGBitmapContextCreateImage (out_bitmap_context);
260
261     /* clean up */
262     CGContextRelease (out_bitmap_context);
263     free (buffer);
264
265     return CAIRO_INT_STATUS_SUCCESS;
266 }
267
268 static cairo_int_status_t
269 _cairo_quartz_convolve_pass (vImage_Buffer *src,
270                              const int16_t *kernel,
271                              int kernel_width, int kernel_height,
272                              const int32_t divisor,
273                              unsigned char *edge_fill,
274                              vImage_Buffer *dst)
275 {
276     vImage_Error error;
277
278     dst->data = malloc (src->rowBytes * src->height);
279     if (! dst->data)
280         return CAIRO_INT_STATUS_NO_MEMORY;
281
282     dst->width = src->width;
283     dst->height = src->height;
284     dst->rowBytes = src->rowBytes;
285
286     /* we always use background color beyond edge */
287     error = vImageConvolve_ARGB8888 (src, dst, NULL, /* no temp buffer */
288                                      0, 0,
289                                      kernel, kernel_width, kernel_height,
290                                      divisor,
291                                      edge_fill,
292                                      kvImageNoFlags);
293
294     if (error != kvImageNoError)
295         return CAIRO_INT_STATUS_UNSUPPORTED;
296
297     return CAIRO_INT_STATUS_SUCCESS;
298 }
299
300 cairo_status_t
301 _cairo_quartz_gaussian_filter (const cairo_pattern_t *src,
302                                const CGImageRef image,
303                                CGImageRef *out_image)
304 {
305     cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS;
306
307     vImage_Buffer src_buffer, dst_buffer;
308     int16_t *kernel = NULL;
309     int32_t divisor;
310     int shrink_factor_x;
311     int shrink_factor_y;
312
313 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
314     CGContextRef image_ctx;
315 #else
316     CGDataProviderRef image_provider;
317     CFDataRef image_data_ref;
318 #endif
319     CGImageRef resized_image;
320     CGImageRef resized_out_image;
321
322     CGContextRef ctx;
323     CGColorSpaceRef color_space;
324     CGBitmapInfo bitmap_info;
325
326     int row, col;
327     unsigned char edge_color[4] = {0, 0, 0, 0};
328
329     if (src->type != CAIRO_PATTERN_TYPE_SURFACE ||
330         ! src->convolution_matrix) {
331         *out_image = CGImageRetain (image);
332         return CAIRO_INT_STATUS_SUCCESS;
333     }
334
335     /* re-compute scaling */
336     kernel = _cairo_quartz_pattern_create_gaussian_matrix ((cairo_pattern_t *)src,
337                                                             &row, &col,
338                                                             &divisor,
339                                                             &shrink_factor_x,
340                                                             &shrink_factor_y);
341     if (! kernel) {
342         *out_image = NULL;
343         return CAIRO_INT_STATUS_NO_MEMORY;
344     }
345
346     if (shrink_factor_x == 1 &&
347         shrink_factor_y == 1)
348         resized_image = CGImageRetain (image);
349     else {
350         status = _cairo_quartz_resize_image (image,
351                                              1.0 / src->shrink_factor_x,
352                                              1.0 / src->shrink_factor_y,
353                                              &resized_image);
354         if (unlikely (status)) {
355             free (kernel);
356             *out_image = NULL;
357             return status;
358         }
359     }
360
361 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
362     image_ctx = _cairo_quartz_get_image_context (resized_image);
363     if (! image_ctx) {
364         free (kernel);
365         *out_image = NULL;
366         return CAIRO_INT_STATUS_NO_MEMORY;
367     }
368 #else
369     image_provider = CGImageGetDataProvider (resized_image);
370     image_data_ref = CGDataProviderCopyData (image_provider);
371 #endif
372
373     src_buffer.width = CGImageGetWidth (resized_image);
374     src_buffer.height = CGImageGetHeight (resized_image);
375     src_buffer.rowBytes = CGImageGetBytesPerRow (resized_image);
376
377 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
378     src_buffer.data = CGBitmapContextGetData (image_ctx);
379     if (! src_buffer.data) {
380         free (kernel);
381         CGContextRelease (image_ctx);
382         *out_image = NULL;
383         return CAIRO_INT_STATUS_NO_MEMORY;
384     }
385 #else
386     src_buffer.data = (void *) CFDataGetBytePtr (image_data_ref);
387 #endif
388
389     dst_buffer.data = NULL;
390
391     status = _cairo_quartz_convolve_pass (&src_buffer,
392                                           kernel,
393                                           col, row,
394                                           divisor,
395                                           edge_color,
396                                           &dst_buffer);
397
398 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
399     CGContextRelease (image_ctx);
400     free (src_buffer.data);
401 #else
402     CFRelease (image_data_ref);
403 #endif
404
405     free (kernel);
406     CGImageRelease (resized_image);
407
408     if (unlikely (status)) {
409         if (dst_buffer.data)
410             free (dst_buffer.data);
411         *out_image = NULL;
412         return status;
413     }
414
415     /* create resized_out_image from blur */
416     color_space = CGImageGetColorSpace (resized_image);
417     bitmap_info = CGImageGetBitmapInfo (resized_image);
418
419     ctx = CGBitmapContextCreate (dst_buffer.data,
420                                  dst_buffer.width,
421                                  dst_buffer.height,
422                                  CGImageGetBitsPerComponent (resized_image),
423                                  dst_buffer.rowBytes,
424                                  color_space,
425                                  bitmap_info);
426
427     resized_out_image = CGBitmapContextCreateImage (ctx);
428
429     CGContextRelease (ctx);
430     free (dst_buffer.data);
431
432     /* scale back from resized_out_image to out_image */
433     if (shrink_factor_x == 1 &&
434         shrink_factor_y == 1) {
435         *out_image = resized_out_image;
436         return CAIRO_INT_STATUS_SUCCESS;
437     }
438
439     status = _cairo_quartz_resize_image (resized_out_image,
440                                          src->shrink_factor_x,
441                                          src->shrink_factor_y,
442                                          out_image);
443     if (unlikely (status)) {
444         CGImageRelease (resized_out_image);
445         *out_image = NULL;
446         return status;
447     }
448     CGImageRelease (resized_out_image);
449     return status;
450 }