1 /*****************************************************************************
2 * This file is part of gfxprim library. *
4 * Gfxprim is free software; you can redistribute it and/or *
5 * modify it under the terms of the GNU Lesser General Public *
6 * License as published by the Free Software Foundation; either *
7 * version 2.1 of the License, or (at your option) any later version. *
9 * Gfxprim is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
12 * Lesser General Public License for more details. *
14 * You should have received a copy of the GNU Lesser General Public *
15 * License along with gfxprim; if not, write to the Free Software *
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
17 * Boston, MA 02110-1301 USA *
19 * Copyright (C) 2009-2012 Cyril Hrubis <metan@ucw.cz> *
21 *****************************************************************************/
25 #include "core/GP_Context.h"
26 #include "core/GP_GetPutPixel.h"
28 #include "core/GP_Debug.h"
30 #include "GP_Linear.h"
32 static inline unsigned int gaussian_kernel_size(float sigma
)
34 int center
= 3 * sigma
;
36 return 2 * center
+ 1;
39 static inline float gaussian_kernel_init(float sigma
, float *kernel
)
41 int i
, center
= 3 * sigma
;
42 int N
= 2 * center
+ 1;
45 double sigma2
= sigma
* sigma
;
47 for (i
= 0; i
< N
; i
++) {
48 double r
= center
- i
;
49 kernel
[i
] = exp(-0.5 * (r
* r
) / sigma2
);
56 static int gaussian_callback_horiz(GP_ProgressCallback
*self
)
58 GP_ProgressCallback
*callback
= self
->priv
;
60 callback
->percentage
= self
->percentage
/ 2;
61 return callback
->callback(callback
);
64 static int gaussian_callback_vert(GP_ProgressCallback
*self
)
66 GP_ProgressCallback
*callback
= self
->priv
;
68 callback
->percentage
= self
->percentage
/ 2 + 50;
69 return callback
->callback(callback
);
72 int GP_FilterGaussianBlur_Raw(const GP_Context
*src
, GP_Context
*dst
,
73 float sigma_x
, float sigma_y
,
74 GP_ProgressCallback
*callback
)
76 unsigned int size_x
= gaussian_kernel_size(sigma_x
);
77 unsigned int size_y
= gaussian_kernel_size(sigma_y
);
79 GP_DEBUG(1, "Gaussian blur sigma_x=%2.3f sigma_y=%2.3f kernel %ix%i image %ux%u",
80 sigma_x
, sigma_y
, size_x
, size_y
, src
->w
, src
->h
);
82 GP_ProgressCallback
*new_callback
= NULL
;
84 GP_ProgressCallback gaussian_callback
= {
85 .callback
= gaussian_callback_horiz
,
90 new_callback
= &gaussian_callback
;
92 /* compute kernel and apply in horizontal direction */
94 float kernel_x
[size_x
];
95 float sum
= gaussian_kernel_init(sigma_x
, kernel_x
);
97 if (GP_FilterHLinearConvolution_Raw(src
, dst
, kernel_x
, size_x
,
102 if (new_callback
!= NULL
)
103 new_callback
->callback
= gaussian_callback_vert
;
105 /* compute kernel and apply in vertical direction */
107 float kernel_y
[size_y
];
108 float sum
= gaussian_kernel_init(sigma_y
, kernel_y
);
110 if (GP_FilterVLinearConvolution_Raw(dst
, dst
, kernel_y
, size_y
,
115 GP_ProgressCallbackDone(callback
);
119 GP_Context
*GP_FilterGaussianBlur(const GP_Context
*src
, GP_Context
*dst
,
120 float sigma_x
, float sigma_y
,
121 GP_ProgressCallback
*callback
)
123 /* TODO: templatetize */
124 if (src
->pixel_type
!= GP_PIXEL_RGB888
)
128 dst
= GP_ContextCopy(src
, 0);
133 GP_ASSERT(src
->pixel_type
== dst
->pixel_type
,
134 "The src and dst pixel types must match");
135 GP_ASSERT(src
->w
<= dst
->w
&& src
->h
<= dst
->h
,
136 "Destination is not big enough");
139 GP_FilterGaussianBlur_Raw(src
, dst
, sigma_x
, sigma_y
, callback
);
146 #define CLAMP(val) do { \
153 int GP_FilterHLinearConvolution_Raw(const GP_Context
*src
, GP_Context
*dst
,
154 float kernel
[], uint32_t kw
, float kern_div
,
155 GP_ProgressCallback
*callback
)
159 int32_t ikernel
[kw
], ikern_div
;
160 uint32_t size
= dst
->w
+ kw
- 1;
162 GP_DEBUG(1, "Horizontal linear convolution kernel width %i image %ux%u",
165 for (i
= 0; i
< kw
; i
++)
166 ikernel
[i
] = kernel
[i
] * MUL
+ 0.5;
168 ikern_div
= kern_div
* MUL
+ 0.5;
170 /* do linear convolution */
171 for (y
= 0; y
< (GP_Coord
)dst
->h
; y
++) {
172 uint8_t R
[size
], G
[size
], B
[size
];
174 /* Fetch the whole row */
175 GP_Pixel pix
= GP_GetPixel_Raw_24BPP(src
, 0, y
);
177 for (i
= 0; i
< kw
/2; i
++) {
178 R
[i
] = GP_Pixel_GET_R_RGB888(pix
);
179 G
[i
] = GP_Pixel_GET_G_RGB888(pix
);
180 B
[i
] = GP_Pixel_GET_B_RGB888(pix
);
183 for (i
= 0; i
< src
->w
; i
++) {
184 pix
= GP_GetPixel_Raw_24BPP(src
, i
, y
);
186 R
[i
+kw
/2] = GP_Pixel_GET_R_RGB888(pix
);
187 G
[i
+kw
/2] = GP_Pixel_GET_G_RGB888(pix
);
188 B
[i
+kw
/2] = GP_Pixel_GET_B_RGB888(pix
);
191 for (i
= src
->w
+ kw
/2; i
< size
; i
++) {
192 R
[i
] = GP_Pixel_GET_R_RGB888(pix
);
193 G
[i
] = GP_Pixel_GET_G_RGB888(pix
);
194 B
[i
] = GP_Pixel_GET_B_RGB888(pix
);
197 for (x
= 0; x
< (GP_Coord
)dst
->w
; x
++) {
198 int32_t r
= MUL
/2, g
= MUL
/2, b
= MUL
/2;
200 /* count the pixel value from neighbours weighted by kernel */
201 for (i
= 0; i
< kw
; i
++) {
202 r
+= R
[i
+ x
] * ikernel
[i
];
203 g
+= G
[i
+ x
] * ikernel
[i
];
204 b
+= B
[i
+ x
] * ikernel
[i
];
207 /* divide the result */
212 /* and clamp just to be extra sure */
217 GP_PutPixel_Raw_24BPP(dst
, x
, y
,
218 GP_Pixel_CREATE_RGB888(r
, g
, b
));
221 if (GP_ProgressCallbackReport(callback
, y
, dst
->h
, dst
->w
))
225 GP_ProgressCallbackDone(callback
);
229 int GP_FilterVLinearConvolution_Raw(const GP_Context
*src
, GP_Context
*dst
,
230 float kernel
[], uint32_t kh
, float kern_div
,
231 GP_ProgressCallback
*callback
)
235 int32_t ikernel
[kh
], ikern_div
;
236 uint32_t size
= dst
->h
+ kh
- 1;
238 for (i
= 0; i
< kh
; i
++)
239 ikernel
[i
] = kernel
[i
] * MUL
+ 0.5;
241 GP_DEBUG(1, "Vertical linear convolution kernel width %i image %ux%u",
244 ikern_div
= kern_div
* MUL
+ 0.5;
246 /* do linear convolution */
247 for (x
= 0; x
< (GP_Coord
)dst
->w
; x
++) {
248 uint8_t R
[size
], G
[size
], B
[size
];
250 /* Fetch the whole column */
251 GP_Pixel pix
= GP_GetPixel_Raw_24BPP(src
, x
, 0);
253 for (i
= 0; i
< kh
/2; i
++) {
254 R
[i
] = GP_Pixel_GET_R_RGB888(pix
);
255 G
[i
] = GP_Pixel_GET_G_RGB888(pix
);
256 B
[i
] = GP_Pixel_GET_B_RGB888(pix
);
259 for (i
= 0; i
< src
->h
; i
++) {
260 pix
= GP_GetPixel_Raw_24BPP(src
, x
, i
);
262 R
[i
+kh
/2] = GP_Pixel_GET_R_RGB888(pix
);
263 G
[i
+kh
/2] = GP_Pixel_GET_G_RGB888(pix
);
264 B
[i
+kh
/2] = GP_Pixel_GET_B_RGB888(pix
);
267 for (i
= src
->h
+ kh
/2; i
< size
; i
++) {
268 R
[i
] = GP_Pixel_GET_R_RGB888(pix
);
269 G
[i
] = GP_Pixel_GET_G_RGB888(pix
);
270 B
[i
] = GP_Pixel_GET_B_RGB888(pix
);
273 for (y
= 0; y
< (GP_Coord
)dst
->h
; y
++) {
274 int32_t r
= MUL
/2, g
= MUL
/2, b
= MUL
/2;
276 /* count the pixel value from neighbours weighted by kernel */
277 for (i
= 0; i
< kh
; i
++) {
278 r
+= R
[y
+ i
] * ikernel
[i
];
279 g
+= G
[y
+ i
] * ikernel
[i
];
280 b
+= B
[y
+ i
] * ikernel
[i
];
283 /* divide the result */
288 /* and clamp just to be extra sure */
293 GP_PutPixel_Raw_24BPP(dst
, x
, y
,
294 GP_Pixel_CREATE_RGB888(r
, g
, b
));
297 if (GP_ProgressCallbackReport(callback
, x
, dst
->w
, dst
->h
))
301 GP_ProgressCallbackDone(callback
);
305 static int h_callback(GP_ProgressCallback
*self
)
307 GP_ProgressCallback
*callback
= self
->priv
;
309 callback
->percentage
= self
->percentage
/ 2;
310 return callback
->callback(callback
);
313 static int v_callback(GP_ProgressCallback
*self
)
315 GP_ProgressCallback
*callback
= self
->priv
;
317 callback
->percentage
= self
->percentage
/ 2 + 50;
318 return callback
->callback(callback
);
321 int GP_FilterVHLinearConvolution_Raw(const GP_Context
*src
, GP_Context
*dst
,
322 float hkernel
[], uint32_t kw
, float hkern_div
,
323 float vkernel
[], uint32_t kh
, float vkern_div
,
324 GP_ProgressCallback
*callback
)
326 GP_ProgressCallback
*new_callback
;
328 GP_ProgressCallback conv_callback
= {
329 .callback
= h_callback
,
333 new_callback
= callback
? &conv_callback
: NULL
;
335 if (GP_FilterVLinearConvolution_Raw(src
, dst
, hkernel
, kw
, hkern_div
, new_callback
))
338 conv_callback
.callback
= v_callback
;
340 if (GP_FilterHLinearConvolution_Raw(dst
, dst
, vkernel
, kh
, vkern_div
, new_callback
))
343 GP_ProgressCallbackDone(callback
);
348 * Linear convolution.
350 * Can be used in-place.
352 int GP_FilterLinearConvolution_Raw(const GP_Context
*src
, GP_Context
*dst
,
353 float kernel
[], uint32_t kw
, uint32_t kh
,
354 float kern_div
, GP_ProgressCallback
*callback
)
359 GP_DEBUG(1, "Linear convolution kernel %ix%i image %ux%u",
360 kw
, kh
, src
->w
, src
->h
);
362 /* do linear convolution */
363 for (y
= 0; y
< (GP_Coord
)dst
->h
; y
++) {
365 uint32_t R
[kw
][kh
], G
[kw
][kh
], B
[kw
][kh
];
367 /* prefill the buffer on the start */
368 for (j
= 0; j
< kh
; j
++) {
369 for (i
= 0; i
< kw
- 1; i
++) {
371 int cy
= y
+ j
- kh
/2;
379 if (cy
>= (int)src
->h
)
382 pix
= GP_GetPixel_Raw_24BPP(src
, cx
, cy
);
384 R
[i
][j
] = GP_Pixel_GET_R_RGB888(pix
);
385 G
[i
][j
] = GP_Pixel_GET_G_RGB888(pix
);
386 B
[i
][j
] = GP_Pixel_GET_B_RGB888(pix
);
392 for (x
= 0; x
< (GP_Coord
)dst
->w
; x
++) {
393 float r
= 0, g
= 0, b
= 0;
395 for (j
= 0; j
< kh
; j
++) {
396 int cy
= y
+ j
- kh
/2;
402 if (cy
>= (int)src
->h
)
405 if (cx
>= (int)src
->w
)
408 pix
= GP_GetPixel_Raw_24BPP(src
, cx
, cy
);
410 R
[idx
][j
] = GP_Pixel_GET_R_RGB888(pix
);
411 G
[idx
][j
] = GP_Pixel_GET_G_RGB888(pix
);
412 B
[idx
][j
] = GP_Pixel_GET_B_RGB888(pix
);
415 /* count the pixel value from neighbours weighted by kernel */
416 for (i
= 0; i
< kw
; i
++) {
419 if ((int)i
< idx
+ 1)
420 k
= kw
- idx
- 1 + i
;
424 for (j
= 0; j
< kh
; j
++) {
425 r
+= R
[i
][j
] * kernel
[k
+ j
* kw
];
426 g
+= G
[i
][j
] * kernel
[k
+ j
* kw
];
427 b
+= B
[i
][j
] * kernel
[k
+ j
* kw
];
431 /* divide the result */
436 /* and clamp just to be extra sure */
450 pix
= GP_Pixel_CREATE_RGB888((uint32_t)r
, (uint32_t)g
, (uint32_t)b
);
452 GP_PutPixel_Raw_24BPP(dst
, x
, y
, pix
);
460 if (GP_ProgressCallbackReport(callback
, y
, dst
->h
, dst
->w
))
464 GP_ProgressCallbackDone(callback
);