loaders: PNG: Handle gamma on 16bpp conversion
[gfxprim.git] / libs / filters / GP_ResizeLinear.gen.c.t
blob1b2bd950e25ab9bb8406fe10486ab1d891893139
1 @ include source.t
2 /*
3  * Linear resampling
4  *
5  * Copyright (C) 2009-2014 Cyril Hrubis <metan@ucw.cz>
6  */
8 #include <string.h>
9 #include <errno.h>
11 #include "core/GP_Pixmap.h"
12 #include "core/GP_GetPutPixel.h"
13 #include "core/GP_Gamma.h"
15 #include "core/GP_Debug.h"
17 #include "GP_Resize.h"
19 @ def fetch_rows(pt, y):
20 for (x = 0; x < src->w; x++) {
21         GP_Pixel pix = GP_GetPixel_Raw_{{ pt.pixelsize.suffix }}(src, x, {{ y }});
22 @     for c in pt.chanslist:
23         {{ c.name }}[x] = GP_Pixel_GET_{{ c.name }}_{{ pt.name }}(pix);
24 @     end
26 @ end
28 @ def sum_rows(pt, mult):
29 for (x = 0; x < dst->w; x++) {
30         /* Get first left pixel */
31 @     for c in pt.chanslist:
32         uint32_t {{ c.name }}_middle = 0;
33         uint32_t {{ c.name }}_first = {{ c.name }}[xmap[x]] * (MULT - xoff[x]);
34 @     end
35         /* Sum middle pixels */
36         for (j = xmap[x]+1; j < xmap[x+1]; j++) {
37 @     for c in pt.chanslist:
38                 {{ c.name }}_middle += {{ c.name }}[j];
39 @     end
40         }
41         /* Add it all together with last pixel on the right */
42 @     for c in pt.chanslist:
43         {{ c.name }}_res[x] += ({{ c.name }}_middle * (MULT / DIV) +
44                                 ({{ c.name }}[xmap[x+1]] * xoff[x+1] +
45                                  {{ c.name }}_first) / DIV) * {{ mult }} / DIV;
46 @     end
47                         }
48 @ end
50 @ for pt in pixeltypes:
51 @     if not pt.is_unknown() and not pt.is_palette():
52 static int resize_lin_lf_{{ pt.name }}(const GP_Pixmap *src, GP_Pixmap *dst,
53                                        GP_ProgressCallback *callback)
55         uint32_t xmap[dst->w + 1];
56         uint32_t ymap[dst->h + 1];
57         uint32_t xoff[dst->w + 1];
58         uint32_t yoff[dst->h + 1];
59 @         for c in pt.chanslist:
60         uint32_t {{ c.name }}[src->w];
61 @         end
62         uint32_t x, y;
63         uint32_t i, j;
64 @ # Reduce fixed point bits for > 8 bits per channel (fixed 16 bit Grayscale)
65 @         if pt.chanslist[0].size > 8:
66         const int MULT=1<<10;
67         const int DIV=1<<6;
68 @         else:
69         const int MULT=1<<14;
70         const int DIV=1<<9;
71 @         end
73         /* Pre-compute mapping for interpolation */
74         for (i = 0; i <= dst->w; i++) {
75                 xmap[i] = ((uint64_t)i * src->w) / dst->w;
76                 xoff[i] = ((uint64_t)MULT * (i * src->w))/dst->w - MULT * xmap[i];
77         }
79         for (i = 0; i <= dst->h; i++) {
80                 ymap[i] = ((uint64_t)i * src->h) / dst->h;
81                 yoff[i] = ((uint64_t)MULT * (i * src->h))/dst->h - MULT * ymap[i];
82         }
84         /* Compute pixel area for the final normalization */
85         uint32_t div = (((uint64_t)(xmap[1] * MULT + xoff[1]) * ((uint64_t)ymap[1] * MULT + yoff[1]) + DIV/2) / DIV + DIV/2)/DIV;
87         /* Prefetch first row */
88         {@ fetch_rows(pt, 0) @}
90         for (y = 0; y < dst->h; y++) {
91 @         for c in pt.chanslist:
92                 uint32_t {{ c.name }}_res[dst->w];
93 @         end
95 @         for c in pt.chanslist:
96                 memset({{ c.name }}_res, 0, sizeof({{ c.name }}_res));
97 @         end
99                 /* Sum first row */
100                 {@ sum_rows(pt, '(MULT-yoff[y])') @}
102                 /* Sum middle */
103                 for (i = ymap[y]+1; i < ymap[y+1]; i++) {
104                         {@ fetch_rows(pt, 'i') @}
105                         {@ sum_rows(pt, 'MULT') @}
106                 }
108                 /* Sum last row */
109                 if (yoff[y+1]) {
110                         {@ fetch_rows(pt, 'ymap[y+1]') @}
111                         {@ sum_rows(pt, 'yoff[y+1]') @}
112                 }
114                 for (x = 0; x < dst->w; x++) {
115 @         for c in pt.chanslist:
116                         uint32_t {{ c.name }}_p = ({{ c.name }}_res[x] + div/2) / div;
117 @         end
118                         GP_PutPixel_Raw_{{ pt.pixelsize.suffix }}(dst, x, y,
119                                 GP_Pixel_CREATE_{{ pt.name }}({{ arr_to_params(pt.chan_names, '', '_p') }}));
120                 }
122                 if (GP_ProgressCallbackReport(callback, y, dst->h, dst->w))
123                         return 1;
124         }
126         GP_ProgressCallbackDone(callback);
127         return 0;
130 @ end
132 @ for pt in pixeltypes:
133 @     if not pt.is_unknown() and not pt.is_palette():
134 static int resize_lin{{ pt.name }}(const GP_Pixmap *src, GP_Pixmap *dst,
135                                    GP_ProgressCallback *callback)
137         uint32_t xmap[dst->w + 1];
138         uint32_t ymap[dst->h + 1];
139         uint8_t  xoff[dst->w + 1];
140         uint8_t  yoff[dst->h + 1];
141         uint32_t x, y, i;
143         GP_DEBUG(1, "Scaling image %ux%u -> %ux%u %2.2f %2.2f",
144                     src->w, src->h, dst->w, dst->h,
145                     1.00 * dst->w / src->w, 1.00 * dst->h / src->h);
147         /* Pre-compute mapping for interpolation */
148         uint32_t xstep = ((src->w - 1) << 16) / (dst->w - 1);
150         for (i = 0; i < dst->w + 1; i++) {
151                 uint32_t val = i * xstep;
152                 xmap[i] = val >> 16;
153                 xoff[i] = (val >> 8) & 0xff;
154         }
156         uint32_t ystep = ((src->h - 1) << 16) / (dst->h - 1);
158         for (i = 0; i < dst->h + 1; i++) {
159                 uint32_t val = i * ystep;
160                 ymap[i] = val >> 16;
161                 yoff[i] = (val >> 8) & 0xff;
162         }
164         /* Interpolate */
165         for (y = 0; y < dst->h; y++) {
166                 for (x = 0; x < dst->w; x++) {
167                         GP_Pixel pix00, pix01, pix10, pix11;
168                         GP_Coord x0, x1, y0, y1;
169 @         for c in pt.chanslist:
170                         uint32_t {{ c[0] }}, {{ c[0] }}0, {{ c[0] }}1;
171 @         end
173                         x0 = xmap[x];
174                         x1 = xmap[x] + 1;
176                         if (x1 >= (GP_Coord)src->w)
177                                 x1 = src->w - 1;
179                         y0 = ymap[y];
180                         y1 = ymap[y] + 1;
182                         if (y1 >= (GP_Coord)src->h)
183                                 y1 = src->h - 1;
185                         pix00 = GP_GetPixel_Raw_{{ pt.pixelsize.suffix }}(src, x0, y0);
186                         pix10 = GP_GetPixel_Raw_{{ pt.pixelsize.suffix }}(src, x1, y0);
187                         pix01 = GP_GetPixel_Raw_{{ pt.pixelsize.suffix }}(src, x0, y1);
188                         pix11 = GP_GetPixel_Raw_{{ pt.pixelsize.suffix }}(src, x1, y1);
190 @         for c in pt.chanslist:
191                         {{ c.name }}0 = GP_Pixel_GET_{{ c.name }}_{{ pt.name }}(pix00) * (255 - xoff[x]);
192 @         end
194 @         for c in pt.chanslist:
195                         {{ c.name }}0 += GP_Pixel_GET_{{ c.name }}_{{ pt.name }}(pix10) * xoff[x];
196 @         end
198 @         for c in pt.chanslist:
199                         {{ c.name }}1 = GP_Pixel_GET_{{ c.name }}_{{ pt.name }}(pix01) * (255 - xoff[x]);
200 @         end
202 @         for c in pt.chanslist:
203                         {{ c.name }}1 += GP_Pixel_GET_{{ c.name }}_{{ pt.name }}(pix11) * xoff[x];
204 @         end
206 @         for c in pt.chanslist:
207                         {{ c.name }} = ({{ c.name }}1 * yoff[y] + {{ c.name }}0 * (255 - yoff[y]) + (1<<15)) >> 16;
208 @         end
210                         GP_PutPixel_Raw_{{ pt.pixelsize.suffix }}(dst, x, y,
211                                               GP_Pixel_CREATE_{{ pt.name }}({{ arr_to_params(pt.chan_names) }}));
212                 }
214                 if (GP_ProgressCallbackReport(callback, y, dst->h, dst->w))
215                         return 1;
216         }
218         GP_ProgressCallbackDone(callback);
219         return 0;
222 @ end
224 static int resize_lin(const GP_Pixmap *src, GP_Pixmap *dst,
225                      GP_ProgressCallback *callback)
227         switch (src->pixel_type) {
228 @ for pt in pixeltypes:
229 @     if not pt.is_unknown() and not pt.is_palette():
230         case GP_PIXEL_{{ pt.name }}:
231                 return resize_lin{{ pt.name }}(src, dst, callback);
232         break;
233 @ end
234         default:
235                 GP_WARN("Invalid pixel type %s",
236                         GP_PixelTypeName(src->pixel_type));
237                 errno = EINVAL;
238                 return -1;
239         }
242 int GP_FilterResizeLinearInt(const GP_Pixmap *src, GP_Pixmap *dst,
243                              GP_ProgressCallback *callback)
245         if (src->pixel_type != dst->pixel_type) {
246                 GP_WARN("The src and dst pixel types must match");
247                 errno = EINVAL;
248                 return 1;
249         }
251         return resize_lin(src, dst, callback);
254 static int resize_lin_lf(const GP_Pixmap *src, GP_Pixmap *dst,
255                           GP_ProgressCallback *callback)
257         float x_rat = 1.00 * dst->w / src->w;
258         float y_rat = 1.00 * dst->h / src->h;
260         if (x_rat < 1.00 && y_rat < 1.00) {
262                 GP_DEBUG(1, "Downscaling image %ux%u -> %ux%u %2.2f %2.2f",
263                              src->w, src->h, dst->w, dst->h, x_rat, y_rat);
265                 switch (src->pixel_type) {
266 @ for pt in pixeltypes:
267 @     if not pt.is_unknown() and not pt.is_palette():
268                 case GP_PIXEL_{{ pt.name }}:
269                         return resize_lin_lf_{{ pt.name }}(src, dst, callback);
270                 break;
271 @ end
272                 default:
273                         GP_WARN("Invalid pixel type %s",
274                                 GP_PixelTypeName(src->pixel_type));
275                         errno = EINVAL;
276                         return -1;
277                 }
278         }
280         //TODO: x_rat > 1.00 && y_rat < 1.00
281         //TODO: x_rat < 1.00 && y_rat > 1.00
283         return resize_lin(src, dst, callback);
286 int GP_FilterResizeLinearLFInt(const GP_Pixmap *src, GP_Pixmap *dst,
287                                GP_ProgressCallback *callback)
289         if (src->pixel_type != dst->pixel_type) {
290                 GP_WARN("The src and dst pixel types must match");
291                 errno = EINVAL;
292                 return 1;
293         }
295         return resize_lin_lf(src, dst, callback);