loaders: PNG: Handle gamma on 16bpp conversion
[gfxprim.git] / libs / filters / GP_LinearConvolution.gen.c.t
blob3e0de14fd54097da948c052ddba7d7d64ce9dfd5
1 @ include source.t
2 /*
3  * Linear Convolution
4  *
5  * Copyright (C) 2009-2014 Cyril Hrubis <metan@ucw.cz>
6  */
8 #include <errno.h>
10 #include "core/GP_Pixmap.h"
11 #include "core/GP_GetPutPixel.h"
12 #include "core/GP_TempAlloc.h"
13 #include "core/GP_Clamp.h"
14 #include "core/GP_Debug.h"
16 #include "GP_Linear.h"
18 #define MUL 1024
20 @ for pt in pixeltypes:
21 @     if not pt.is_unknown() and not pt.is_palette():
23 static int h_lin_conv_{{ pt.name }}(const GP_Pixmap *src,
24                                     GP_Coord x_src, GP_Coord y_src,
25                                     GP_Size w_src, GP_Size h_src,
26                                     GP_Pixmap *dst,
27                                     GP_Coord x_dst, GP_Coord y_dst,
28                                     float kernel[], uint32_t kw, float kern_div,
29                                     GP_ProgressCallback *callback)
31         GP_Coord x, y;
32         uint32_t i;
33         int ikernel[kw], ikern_div;
34         uint32_t size = w_src + kw - 1;
36         for (i = 0; i < kw; i++)
37                 ikernel[i] = kernel[i] * MUL + 0.5;
39         ikern_div = kern_div * MUL + 0.5;
41         /* Create temporary buffers */
42         GP_TempAllocCreate(temp, {{ len(pt.chanslist) }} * size * sizeof(int));
44 @         for c in pt.chanslist:
45         int *{{ c.name }} = GP_TempAllocGet(temp, size * sizeof(int));
46 @         end
48         /* Do horizontal linear convolution */
49         for (y = 0; y < (GP_Coord)h_src; y++) {
50                 int yi = GP_MIN(y_src + y, (int)src->h - 1);
52                 /* Fetch the whole row */
53                 GP_Pixel pix = GP_GetPixel_Raw_{{ pt.pixelsize.suffix }}(src, 0, yi);
55                 int xi = x_src - kw/2;
56                 i = 0;
58                 /* Copy border pixel until the source image starts */
59                 while (xi <= 0 && i < size) {
60 @         for c in pt.chanslist:
61                         {{ c.name }}[i] = GP_Pixel_GET_{{ c.name }}_{{ pt.name }}(pix);
62 @         end
63                         i++;
64                         xi++;
65                 }
67                 /* Use as much source image pixels as possible */
68                 while (xi < (int)src->w && i < size) {
69                         pix = GP_GetPixel_Raw_{{ pt.pixelsize.suffix }}(src, xi, yi);
71 @         for c in pt.chanslist:
72                         {{ c.name }}[i] = GP_Pixel_GET_{{ c.name }}_{{ pt.name }}(pix);
73 @         end
75                         i++;
76                         xi++;
77                 }
79                 /* Copy the rest the border pixel when we are out again */
80                 while (i < size) {
81 @         for c in pt.chanslist:
82                         {{ c.name }}[i] = GP_Pixel_GET_{{ c.name }}_{{ pt.name }}(pix);
83 @         end
85                         i++;
86                 }
88                 for (x = 0; x < (GP_Coord)w_src; x++) {
89 @         for c in pt.chanslist:
90                         int32_t {{ c.name }}_sum = MUL/2;
91                         int *p{{ c.name }} = {{ c.name }} + x;
92 @         end
94                         /* count the pixel value from neighbours weighted by kernel */
95                         for (i = 0; i < kw; i++) {
96 @         for c in pt.chanslist:
97                                 {{ c.name }}_sum += (*p{{ c.name }}++) * ikernel[i];
98 @         end
99                         }
101                         /* divide the result */
102 @         for c in pt.chanslist:
103                         {{ c.name }}_sum /= ikern_div;
104 @         end
106                         /* and clamp just to be extra sure */
107 @         for c in pt.chanslist:
108                         {{ c.name }}_sum = GP_CLAMP({{ c.name }}_sum, 0, {{ c.max }});
109 @         end
110                         GP_PutPixel_Raw_{{ pt.pixelsize.suffix }}(dst, x_dst + x, y_dst + y,
111                                               GP_Pixel_CREATE_{{ pt.name }}(
112                                               {{ arr_to_params(pt.chan_names, "", "_sum") }}
113                                               ));
114                 }
116                 if (GP_ProgressCallbackReport(callback, y, h_src, w_src)) {
117                         GP_TempAllocFree(temp);
118                         return 1;
119                 }
120         }
122         GP_TempAllocFree(temp);
124         GP_ProgressCallbackDone(callback);
125         return 0;
128 @ end
130 int GP_FilterHLinearConvolution_Raw(const GP_Pixmap *src,
131                                     GP_Coord x_src, GP_Coord y_src,
132                                     GP_Size w_src, GP_Size h_src,
133                                     GP_Pixmap *dst,
134                                     GP_Coord x_dst, GP_Coord y_dst,
135                                     float kernel[], uint32_t kw, float kern_div,
136                                     GP_ProgressCallback *callback)
138         GP_DEBUG(1, "Horizontal linear convolution kernel width %u "
139                     "offset %ix%i rectangle %ux%u",
140                     kw, x_src, y_src, w_src, h_src);
142         switch (src->pixel_type) {
143 @ for pt in pixeltypes:
144 @     if not pt.is_unknown() and not pt.is_palette():
145         case GP_PIXEL_{{ pt.name }}:
146                 return h_lin_conv_{{ pt.name }}(src, x_src, y_src, w_src, h_src,
147                                                 dst, x_dst, y_dst,
148                                                 kernel, kw, kern_div, callback);
149         break;
150 @ end
151         default:
152                 errno = EINVAL;
153                 return -1;
154         }
157 @ for pt in pixeltypes:
158 @     if not pt.is_unknown() and not pt.is_palette():
160 static int v_lin_conv_{{ pt.name }}(const GP_Pixmap *src,
161                                     GP_Coord x_src, GP_Coord y_src,
162                                     GP_Size w_src, GP_Size h_src,
163                                     GP_Pixmap *dst,
164                                     GP_Coord x_dst, GP_Coord y_dst,
165                                     float kernel[], uint32_t kh, float kern_div,
166                                     GP_ProgressCallback *callback)
168         GP_Coord x, y;
169         uint32_t i;
170         int ikernel[kh], ikern_div;
171         uint32_t size = h_src + kh - 1;
173         for (i = 0; i < kh; i++)
174                 ikernel[i] = kernel[i] * MUL + 0.5;
176         ikern_div = kern_div * MUL + 0.5;
178         /* Create temporary buffers */
179         GP_TempAllocCreate(temp, {{ len(pt.chanslist) }} * size * sizeof(int));
181 @         for c in pt.chanslist:
182         int *{{ c.name }} = GP_TempAllocGet(temp, size * sizeof(int));
183 @         end
185         /* Do vertical linear convolution */
186         for (x = 0; x < (GP_Coord)w_src; x++) {
187                 int xi = GP_MIN(x_src + x, (int)src->w - 1);
189                 /* Fetch the whole row */
190                 GP_Pixel pix = GP_GetPixel_Raw_{{ pt.pixelsize.suffix }}(src, xi, 0);
192                 int yi = y_src - kh/2;
193                 i = 0;
195                 /* Copy border pixel until the source image starts */
196                 while (yi <= 0 && i < size) {
197 @         for c in pt.chanslist:
198                         {{ c.name }}[i] = GP_Pixel_GET_{{ c.name }}_{{ pt.name }}(pix);
199 @         end
201                         i++;
202                         yi++;
203                 }
205                 /* Use as much source image pixels as possible */
206                 while (yi < (int)src->h && i < size) {
207                         pix = GP_GetPixel_Raw_{{ pt.pixelsize.suffix }}(src, xi, yi);
209 @         for c in pt.chanslist:
210                         {{ c.name }}[i] = GP_Pixel_GET_{{ c.name }}_{{ pt.name }}(pix);
211 @         end
213                         i++;
214                         yi++;
215                 }
217                 /* Copy the rest the border pixel when we are out again */
218                 while (i < size) {
219 @         for c in pt.chanslist:
220                         {{ c.name }}[i] = GP_Pixel_GET_{{ c.name }}_{{ pt.name }}(pix);
221 @         end
223                         i++;
224                 }
226                 for (y = 0; y < (GP_Coord)h_src; y++) {
227 @         for c in pt.chanslist:
228                         int32_t {{ c.name }}_sum = MUL/2;
229                         int *p{{ c.name }} = {{ c.name }} + y;
230 @         end
232                         /* count the pixel value from neighbours weighted by kernel */
233                         for (i = 0; i < kh; i++) {
234 @         for c in pt.chanslist:
235                                 {{ c.name }}_sum += (*p{{ c.name }}++) * ikernel[i];
236 @         end
237                         }
239                         /* divide the result */
240 @         for c in pt.chanslist:
241                         {{ c.name }}_sum /= ikern_div;
242 @         end
244                         /* and clamp just to be extra sure */
245 @         for c in pt.chanslist:
246                         {{ c.name }}_sum = GP_CLAMP({{ c.name }}_sum, 0, {{ c.max }});
247 @         end
249                         GP_PutPixel_Raw_{{ pt.pixelsize.suffix }}(dst, x_dst + x, y_dst + y,
250                                               GP_Pixel_CREATE_{{ pt.name }}(
251                                               {{ arr_to_params(pt.chan_names, "", "_sum") }}
252                                               ));
253                 }
255                 if (GP_ProgressCallbackReport(callback, x, w_src, h_src)) {
256                         GP_TempAllocFree(temp);
257                         return 1;
258                 }
259         }
261         GP_TempAllocFree(temp);
263         GP_ProgressCallbackDone(callback);
264         return 0;
267 @ end
269 int GP_FilterVLinearConvolution_Raw(const GP_Pixmap *src,
270                                     GP_Coord x_src, GP_Coord y_src,
271                                     GP_Size w_src, GP_Size h_src,
272                                     GP_Pixmap *dst,
273                                     GP_Coord x_dst, GP_Coord y_dst,
274                                     float kernel[], uint32_t kh, float kern_div,
275                                     GP_ProgressCallback *callback)
277         GP_DEBUG(1, "Vertical linear convolution kernel width %u "
278                     "offset %ix%i rectangle %ux%u",
279                     kh, x_src, y_src, w_src, h_src);
281         switch (src->pixel_type) {
282 @ for pt in pixeltypes:
283 @     if not pt.is_unknown() and not pt.is_palette():
284         case GP_PIXEL_{{ pt.name }}:
285                 return v_lin_conv_{{ pt.name }}(src, x_src, y_src, w_src, h_src,
286                                                 dst, x_dst, y_dst,
287                                                 kernel, kh, kern_div, callback);
288         break;
289 @ end
290         default:
291                 errno = EINVAL;
292                 return -1;
293         }
296 @ for pt in pixeltypes:
297 @     if not pt.is_unknown() and not pt.is_palette():
299 static int lin_conv_{{ pt.name }}(const GP_Pixmap *src,
300                                   GP_Coord x_src, GP_Coord y_src,
301                                   GP_Size w_src, GP_Size h_src,
302                                   GP_Pixmap *dst,
303                                   GP_Coord x_dst, GP_Coord y_dst,
304                                   float kernel[], uint32_t kw, uint32_t kh,
305                                   float kern_div, GP_ProgressCallback *callback)
307         GP_Coord x, y;
308         unsigned int i, j;
310         /* Do linear convolution */
311         for (y = 0; y < (GP_Coord)h_src; y++) {
312 @         for c in pt.chanslist:
313                 uint32_t {{ c.name }}[kw][kh];
314 @         end
315                 GP_Pixel pix;
317                 /* Prefill the buffer on the start */
318                 for (j = 0; j < kh; j++) {
319                         for (i = 0; i < kw - 1; i++) {
320                                 int xi = x_src + i - kw/2;
321                                 int yi = y_src + y + j - kh/2;
323                                 xi = GP_CLAMP(xi, 0, (int)src->w - 1);
324                                 yi = GP_CLAMP(yi, 0, (int)src->h - 1);
326                                 pix = GP_GetPixel_Raw_{{ pt.pixelsize.suffix }}(src, xi, yi);
328 @         for c in pt.chanslist:
329                                 {{ c.name }}[i][j] = GP_Pixel_GET_{{ c.name }}_{{ pt.name }}(pix);
330 @         end
331                         }
332                 }
334                 int idx = kw - 1;
336                 for (x = 0; x < (GP_Coord)w_src; x++) {
337 @         for c in pt.chanslist:
338                         float {{ c.name }}_sum = 0;
339 @         end
341                         for (j = 0; j < kh; j++) {
342                                 int xi = x_src + x + kw/2;
343                                 int yi = y_src + y + j - kh/2;
345                                 xi = GP_CLAMP(xi, 0, (int)src->w - 1);
346                                 yi = GP_CLAMP(yi, 0, (int)src->h - 1);
348                                 pix = GP_GetPixel_Raw_{{ pt.pixelsize.suffix }}(src, xi, yi);
350 @         for c in pt.chanslist:
351                                 {{ c.name }}[idx][j] = GP_Pixel_GET_{{ c.name }}_{{ pt.name }}(pix);
352 @         end
353                         }
355                         /* Count the pixel value from neighbours weighted by kernel */
356                         for (i = 0; i < kw; i++) {
357                                 int k;
359                                 if ((int)i < idx + 1)
360                                         k = kw - idx - 1 + i;
361                                 else
362                                         k = i - idx - 1;
364                                 for (j = 0; j < kh; j++) {
365 @         for c in pt.chanslist:
366                                         {{ c.name }}_sum += {{ c.name }}[i][j] * kernel[k + j * kw];
367 @         end
368                                 }
369                         }
371                         /* divide the result */
372 @         for c in pt.chanslist:
373                         {{ c.name }}_sum /= kern_div;
374 @         end
376                         /* and clamp just to be extra sure */
377 @         for c in pt.chanslist:
378                         int {{ c.name }}_res = GP_CLAMP((int){{ c.name }}_sum, 0, {{ c.max }});
379 @         end
381                         pix = GP_Pixel_CREATE_{{ pt.name }}({{ arr_to_params(pt.chan_names, "", "_res") }});
383                         GP_PutPixel_Raw_{{ pt.pixelsize.suffix }}(dst, x_dst + x, y_dst + y, pix);
385                         idx++;
387                         if (idx >= (int)kw)
388                                 idx = 0;
389                 }
391                 if (GP_ProgressCallbackReport(callback, y, h_src, w_src))
392                         return 1;
393         }
395         GP_ProgressCallbackDone(callback);
396         return 0;
399 @ end
401 int GP_FilterLinearConvolution_Raw(const GP_Pixmap *src,
402                                    GP_Coord x_src, GP_Coord y_src,
403                                    GP_Size w_src, GP_Size h_src,
404                                    GP_Pixmap *dst,
405                                    GP_Coord x_dst, GP_Coord y_dst,
406                                    float kernel[], uint32_t kw, uint32_t kh,
407                                    float kern_div, GP_ProgressCallback *callback)
409         GP_DEBUG(1, "Linear convolution kernel %ix%i rectangle %ux%u",
410                     kw, kh, w_src, h_src);
412         switch (src->pixel_type) {
413 @ for pt in pixeltypes:
414 @     if not pt.is_unknown() and not pt.is_palette():
415         case GP_PIXEL_{{ pt.name }}:
416                 return lin_conv_{{ pt.name }}(src, x_src, y_src, w_src, h_src,
417                                               dst, x_dst, y_dst,
418                                               kernel, kw, kh, kern_div, callback);
419         break;
420 @ end
421         default:
422                 errno = EINVAL;
423                 return -1;
424         }