filters: Add generated Nearest Neighbour filter.
[gfxprim.git] / libs / filters / GP_Resize.c
bloba1c0d713548909543e0abf7717be6e71299924a6
1 /*****************************************************************************
2 * This file is part of gfxprim library. *
3 * *
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. *
8 * *
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. *
13 * *
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 *
18 * *
19 * Copyright (C) 2009-2012 Cyril Hrubis <metan@ucw.cz> *
20 * *
21 *****************************************************************************/
23 #include <math.h>
25 #include "core/GP_Context.h"
26 #include "core/GP_GetPutPixel.h"
27 #include "core/GP_Gamma.h"
29 #include "core/GP_Debug.h"
31 #include "GP_Resize.h"
33 int GP_FilterResizeNN_Raw(const GP_Context *src, GP_Context *dst,
34 GP_ProgressCallback *callback);
36 int GP_FilterResizeNN(const GP_Context *src, GP_Context *dst,
37 GP_ProgressCallback *callback)
39 GP_ASSERT(src->pixel_type == dst->pixel_type);
41 return GP_FilterResizeNN_Raw(src, dst, callback);
44 GP_Context *GP_FilterResizeNNAlloc(const GP_Context *src,
45 GP_Size w, GP_Size h,
46 GP_ProgressCallback *callback)
48 GP_Context *res = GP_ContextAlloc(w, h, src->pixel_type);
50 if (res == NULL)
51 return NULL;
53 if (GP_FilterResizeNN_Raw(src, res, callback)) {
54 GP_ContextFree(res);
55 return NULL;
58 return res;
62 #define A 0.5
64 static float cubic(float x)
66 if (x < 0)
67 x = -x;
69 if (x < 1)
70 return (2 - A)*x*x*x + (A - 3)*x*x + 1;
72 if (x < 2)
73 return -A*x*x*x + 5*A*x*x - 8*A*x + 4*A;
75 return 0;
78 typedef float v4sf __attribute__ ((vector_size (sizeof(float) * 4)));
80 typedef union v4f {
81 v4sf v;
82 float f[4];
83 } v4f;
85 #define GP_USE_GCC_VECTOR
87 #ifdef GP_USE_GCC_VECTOR
88 #define MUL_V4SF(a, b) ({v4f ret; ret.v = (a).v * (b).v; ret;})
89 #else
90 #define MUL_V4SF(a, b) ({v4f ret; \
91 ret.f[0] = (a).f[0] * (b).f[0]; \
92 ret.f[1] = (a).f[1] * (b).f[1]; \
93 ret.f[2] = (a).f[2] * (b).f[2]; \
94 ret.f[3] = (a).f[3] * (b).f[3]; \
95 ret;})
96 #endif /* GP_USE_GCC_VECTOR */
98 #define SUM_V4SF(a) ((a).f[0] + (a).f[1] + (a).f[2] + (a).f[3])
100 #define CLAMP(val) do { \
101 if (val < 0) \
102 val = 0; \
103 if (val > 255) \
104 val = 255; \
105 } while (0)
107 int GP_FilterInterpolate_Cubic(const GP_Context *src, GP_Context *dst,
108 GP_ProgressCallback *callback)
110 float col_r[src->h], col_g[src->h], col_b[src->h];
111 uint32_t i, j;
113 GP_DEBUG(1, "Scaling image %ux%u -> %ux%u %2.2f %2.2f",
114 src->w, src->h, dst->w, dst->h,
115 1.00 * dst->w / src->w, 1.00 * dst->h / src->h);
117 for (i = 0; i < dst->w; i++) {
118 float x = (1.00 * i / dst->w) * src->w;
119 v4f cvx;
120 int xi[4];
122 xi[0] = floor(x - 1);
123 xi[1] = x;
124 xi[2] = x + 1;
125 xi[3] = x + 2;
127 cvx.f[0] = cubic(xi[0] - x);
128 cvx.f[1] = cubic(xi[1] - x);
129 cvx.f[2] = cubic(xi[2] - x);
130 cvx.f[3] = cubic(xi[3] - x);
132 if (xi[0] < 0)
133 xi[0] = 0;
135 if (xi[2] >= (int)src->w)
136 xi[2] = src->w - 1;
138 if (xi[3] >= (int)src->w)
139 xi[3] = src->w - 1;
141 /* Generate interpolated column */
142 for (j = 0; j < src->h; j++) {
143 v4f rv, gv, bv;
144 GP_Pixel pix[4];
146 pix[0] = GP_GetPixel_Raw_24BPP(src, xi[0], j);
147 pix[1] = GP_GetPixel_Raw_24BPP(src, xi[1], j);
148 pix[2] = GP_GetPixel_Raw_24BPP(src, xi[2], j);
149 pix[3] = GP_GetPixel_Raw_24BPP(src, xi[3], j);
151 rv.f[0] = GP_Pixel_GET_R_RGB888(pix[0]);
152 rv.f[1] = GP_Pixel_GET_R_RGB888(pix[1]);
153 rv.f[2] = GP_Pixel_GET_R_RGB888(pix[2]);
154 rv.f[3] = GP_Pixel_GET_R_RGB888(pix[3]);
156 gv.f[0] = GP_Pixel_GET_G_RGB888(pix[0]);
157 gv.f[1] = GP_Pixel_GET_G_RGB888(pix[1]);
158 gv.f[2] = GP_Pixel_GET_G_RGB888(pix[2]);
159 gv.f[3] = GP_Pixel_GET_G_RGB888(pix[3]);
161 bv.f[0] = GP_Pixel_GET_B_RGB888(pix[0]);
162 bv.f[1] = GP_Pixel_GET_B_RGB888(pix[1]);
163 bv.f[2] = GP_Pixel_GET_B_RGB888(pix[2]);
164 bv.f[3] = GP_Pixel_GET_B_RGB888(pix[3]);
166 rv = MUL_V4SF(rv, cvx);
167 gv = MUL_V4SF(gv, cvx);
168 bv = MUL_V4SF(bv, cvx);
170 col_r[j] = SUM_V4SF(rv);
171 col_g[j] = SUM_V4SF(gv);
172 col_b[j] = SUM_V4SF(bv);
175 /* now interpolate column for new image */
176 for (j = 0; j < dst->h; j++) {
177 float y = (1.00 * j / dst->h) * src->h;
178 v4f cvy, rv, gv, bv;
179 float r, g, b;
180 int yi[4];
182 yi[0] = floor(y - 1);
183 yi[1] = y;
184 yi[2] = y + 1;
185 yi[3] = y + 2;
187 cvy.f[0] = cubic(yi[0] - y);
188 cvy.f[1] = cubic(yi[1] - y);
189 cvy.f[2] = cubic(yi[2] - y);
190 cvy.f[3] = cubic(yi[3] - y);
192 if (yi[0] < 0)
193 yi[0] = 0;
195 if (yi[2] >= (int)src->h)
196 yi[2] = src->h - 1;
198 if (yi[3] >= (int)src->h)
199 yi[3] = src->h - 1;
201 rv.f[0] = col_r[yi[0]];
202 rv.f[1] = col_r[yi[1]];
203 rv.f[2] = col_r[yi[2]];
204 rv.f[3] = col_r[yi[3]];
206 gv.f[0] = col_g[yi[0]];
207 gv.f[1] = col_g[yi[1]];
208 gv.f[2] = col_g[yi[2]];
209 gv.f[3] = col_g[yi[3]];
211 bv.f[0] = col_b[yi[0]];
212 bv.f[1] = col_b[yi[1]];
213 bv.f[2] = col_b[yi[2]];
214 bv.f[3] = col_b[yi[3]];
216 rv = MUL_V4SF(rv, cvy);
217 gv = MUL_V4SF(gv, cvy);
218 bv = MUL_V4SF(bv, cvy);
220 r = SUM_V4SF(rv);
221 g = SUM_V4SF(gv);
222 b = SUM_V4SF(bv);
224 CLAMP(r);
225 CLAMP(g);
226 CLAMP(b);
228 GP_Pixel pix = GP_Pixel_CREATE_RGB888((uint8_t)r, (uint8_t)g, (uint8_t)b);
229 GP_PutPixel_Raw_24BPP(dst, i, j, pix);
232 if (GP_ProgressCallbackReport(callback, i, dst->w, dst->h))
233 return 1;
236 GP_ProgressCallbackDone(callback);
237 return 0;
240 #define MUL 1024
242 #define MUL_I(a, b) ({ \
243 a[0] *= b[0]; \
244 a[1] *= b[1]; \
245 a[2] *= b[2]; \
246 a[3] *= b[3]; \
249 #define SUM_I(a) \
250 ((a)[0] + (a)[1] + (a)[2] + (a)[3])
252 #include "core/GP_GammaCorrection.h"
254 int GP_FilterInterpolate_CubicInt(const GP_Context *src, GP_Context *dst,
255 GP_ProgressCallback *callback)
257 int32_t col_r[src->w], col_g[src->w], col_b[src->w];
258 uint32_t i, j;
260 GP_DEBUG(1, "Scaling image %ux%u -> %ux%u %2.2f %2.2f",
261 src->w, src->h, dst->w, dst->h,
262 1.00 * dst->w / src->w, 1.00 * dst->h / src->h);
264 uint16_t *R_2_LIN = NULL;
265 uint16_t *G_2_LIN = NULL;
266 uint16_t *B_2_LIN = NULL;
268 uint8_t *R_2_GAMMA = NULL;
269 uint8_t *G_2_GAMMA = NULL;
270 uint8_t *B_2_GAMMA = NULL;
272 if (src->gamma) {
273 R_2_LIN = src->gamma->tables[0]->u16;
274 G_2_LIN = src->gamma->tables[1]->u16;
275 B_2_LIN = src->gamma->tables[2]->u16;
277 R_2_GAMMA = src->gamma->tables[3]->u8;
278 G_2_GAMMA = src->gamma->tables[4]->u8;
279 B_2_GAMMA = src->gamma->tables[5]->u8;
282 for (i = 0; i < dst->h; i++) {
283 float y = (1.00 * i / dst->h) * src->h;
284 int32_t cvy[4];
285 int yi[4];
287 yi[0] = floor(y - 1);
288 yi[1] = y;
289 yi[2] = y + 1;
290 yi[3] = y + 2;
292 cvy[0] = cubic(yi[0] - y) * MUL + 0.5;
293 cvy[1] = cubic(yi[1] - y) * MUL + 0.5;
294 cvy[2] = cubic(yi[2] - y) * MUL + 0.5;
295 cvy[3] = cubic(yi[3] - y) * MUL + 0.5;
297 if (yi[0] < 0)
298 yi[0] = 0;
300 if (yi[2] >= (int)src->h)
301 yi[2] = src->h - 1;
303 if (yi[3] >= (int)src->h)
304 yi[3] = src->h - 1;
306 /* Generate interpolated row */
307 for (j = 0; j < src->w; j++) {
308 int32_t rv[4], gv[4], bv[4];
309 GP_Pixel pix[4];
311 pix[0] = GP_GetPixel_Raw_24BPP(src, j, yi[0]);
312 pix[1] = GP_GetPixel_Raw_24BPP(src, j, yi[1]);
313 pix[2] = GP_GetPixel_Raw_24BPP(src, j, yi[2]);
314 pix[3] = GP_GetPixel_Raw_24BPP(src, j, yi[3]);
316 rv[0] = GP_Pixel_GET_R_RGB888(pix[0]);
317 rv[1] = GP_Pixel_GET_R_RGB888(pix[1]);
318 rv[2] = GP_Pixel_GET_R_RGB888(pix[2]);
319 rv[3] = GP_Pixel_GET_R_RGB888(pix[3]);
321 gv[0] = GP_Pixel_GET_G_RGB888(pix[0]);
322 gv[1] = GP_Pixel_GET_G_RGB888(pix[1]);
323 gv[2] = GP_Pixel_GET_G_RGB888(pix[2]);
324 gv[3] = GP_Pixel_GET_G_RGB888(pix[3]);
326 bv[0] = GP_Pixel_GET_B_RGB888(pix[0]);
327 bv[1] = GP_Pixel_GET_B_RGB888(pix[1]);
328 bv[2] = GP_Pixel_GET_B_RGB888(pix[2]);
329 bv[3] = GP_Pixel_GET_B_RGB888(pix[3]);
332 if (src->gamma) {
333 rv[0] = R_2_LIN[rv[0]];
334 rv[1] = R_2_LIN[rv[1]];
335 rv[2] = R_2_LIN[rv[2]];
336 rv[3] = R_2_LIN[rv[3]];
338 gv[0] = G_2_LIN[gv[0]];
339 gv[1] = G_2_LIN[gv[1]];
340 gv[2] = G_2_LIN[gv[2]];
341 gv[3] = G_2_LIN[gv[3]];
343 bv[0] = G_2_LIN[bv[0]];
344 bv[1] = G_2_LIN[bv[1]];
345 bv[2] = G_2_LIN[bv[2]];
346 bv[3] = G_2_LIN[bv[3]];
349 MUL_I(rv, cvy);
350 MUL_I(gv, cvy);
351 MUL_I(bv, cvy);
353 col_r[j] = SUM_I(rv);
354 col_g[j] = SUM_I(gv);
355 col_b[j] = SUM_I(bv);
358 /* now interpolate column for new image */
359 for (j = 0; j < dst->w; j++) {
360 float x = (1.00 * j / dst->w) * src->w;
361 int32_t cvx[4], rv[4], gv[4], bv[4];
362 int32_t r, g, b;
363 int xi[4];
365 xi[0] = floor(x - 1);
366 xi[1] = x;
367 xi[2] = x + 1;
368 xi[3] = x + 2;
370 cvx[0] = cubic(xi[0] - x) * MUL + 0.5;
371 cvx[1] = cubic(xi[1] - x) * MUL + 0.5;
372 cvx[2] = cubic(xi[2] - x) * MUL + 0.5;
373 cvx[3] = cubic(xi[3] - x) * MUL + 0.5;
375 if (xi[0] < 0)
376 xi[0] = 0;
378 if (xi[2] >= (int)src->w)
379 xi[2] = src->w - 1;
381 if (xi[3] >= (int)src->w)
382 xi[3] = src->w - 1;
384 rv[0] = col_r[xi[0]];
385 rv[1] = col_r[xi[1]];
386 rv[2] = col_r[xi[2]];
387 rv[3] = col_r[xi[3]];
389 gv[0] = col_g[xi[0]];
390 gv[1] = col_g[xi[1]];
391 gv[2] = col_g[xi[2]];
392 gv[3] = col_g[xi[3]];
394 bv[0] = col_b[xi[0]];
395 bv[1] = col_b[xi[1]];
396 bv[2] = col_b[xi[2]];
397 bv[3] = col_b[xi[3]];
399 MUL_I(rv, cvx);
400 MUL_I(gv, cvx);
401 MUL_I(bv, cvx);
403 r = (SUM_I(rv) + MUL*MUL/2) / MUL / MUL;
404 g = (SUM_I(gv) + MUL*MUL/2) / MUL / MUL;
405 b = (SUM_I(bv) + MUL*MUL/2) / MUL / MUL;
407 if (src->gamma) {
408 if (r > 1023)
409 r = 1023;
410 if (g > 1023)
411 g = 1023;
412 if (b > 1023)
413 b = 1023;
415 if (r < 0)
416 r = 0;
417 if (g < 0)
418 g = 0;
419 if (b < 0)
420 b = 0;
422 r = R_2_GAMMA[r];
423 g = G_2_GAMMA[g];
424 b = B_2_GAMMA[b];
425 } else {
426 CLAMP(r);
427 CLAMP(g);
428 CLAMP(b);
431 GP_Pixel pix = GP_Pixel_CREATE_RGB888((uint8_t)r, (uint8_t)g, (uint8_t)b);
432 GP_PutPixel_Raw_24BPP(dst, j, i, pix);
435 if (GP_ProgressCallbackReport(callback, i, dst->h, dst->w))
436 return 1;
439 GP_ProgressCallbackDone(callback);
440 return 0;
444 * Sample row.
446 * The x and y are starting coordinates in source image.
448 * The xpix_dist is distance of two sampled pixels in source image coordinates.
450 * The xoff is offset of the first pixel.
452 * The r, g, b are used to store resulting values.
454 static inline void linear_lp_sample_x(const GP_Context *src,
455 uint32_t x, uint32_t y,
456 uint32_t xpix_dist, uint32_t xoff,
457 uint32_t *r, uint32_t *g, uint32_t *b)
459 GP_Pixel pix;
460 uint32_t i;
462 pix = GP_GetPixel_Raw_24BPP(src, x, y);
464 *r = (GP_Pixel_GET_R_RGB888(pix) * xoff) >> 9;
465 *g = (GP_Pixel_GET_G_RGB888(pix) * xoff) >> 9;
466 *b = (GP_Pixel_GET_B_RGB888(pix) * xoff) >> 9;
468 for (i = (1<<14) - xoff; i > xpix_dist; i -= xpix_dist) {
469 if (x < src->w - 1)
470 x++;
472 pix = GP_GetPixel_Raw_24BPP(src, x, y);
474 *r += (GP_Pixel_GET_R_RGB888(pix) * xpix_dist) >> 9;
475 *g += (GP_Pixel_GET_G_RGB888(pix) * xpix_dist) >> 9;
476 *b += (GP_Pixel_GET_B_RGB888(pix) * xpix_dist) >> 9;
479 if (i > 0) {
480 if (x < src->w - 1)
481 x++;
483 pix = GP_GetPixel_Raw_24BPP(src, x, y);
485 *r += (GP_Pixel_GET_R_RGB888(pix) * i) >> 9;
486 *g += (GP_Pixel_GET_G_RGB888(pix) * i) >> 9;
487 *b += (GP_Pixel_GET_B_RGB888(pix) * i) >> 9;
492 * Linear interpolation with low-pass filtering, used for fast downscaling
493 * on both x and y.
495 * Basically we do weighted arithmetic mean of the pixels:
497 * [x, y], [x + 1, y], [x + 2, y] ... [x + k, y]
498 * [x, y + 1]
499 * [x, y + 2] .
500 * . . .
501 * . . .
502 * . .
503 * [x, y + l] .... [x + k, y + l]
506 * The parameter k respectively l is determined by the distance between
507 * sampled coordinates on x respectively y.
509 * The pixels are weighted by how much they are 'hit' by the rectangle defined
510 * by the sampled pixel.
512 * The implementation is inspired by imlib2 downscaling algorithm.
514 static int interpolate_linear_lp_xy(const GP_Context *src, GP_Context *dst,
515 GP_ProgressCallback *callback)
517 uint32_t xmap[dst->w + 1];
518 uint32_t ymap[dst->h + 1];
519 uint16_t xoff[dst->w + 1];
520 uint16_t yoff[dst->h + 1];
521 uint32_t x, y;
522 uint32_t i, j;
524 /* Pre-compute mapping for interpolation */
525 uint32_t xstep = (src->w << 16) / dst->w;
526 uint32_t xpix_dist = (dst->w << 14) / src->w;
528 for (i = 0; i < dst->w + 1; i++) {
529 uint32_t val = i * xstep;
530 xmap[i] = val >> 16;
531 xoff[i] = ((255 - ((val >> 8) & 0xff)) * xpix_dist)>>8;
534 uint32_t ystep = (src->h << 16) / dst->h;
535 uint32_t ypix_dist = (dst->h << 14) / src->h;
537 for (i = 0; i < dst->h + 1; i++) {
538 uint32_t val = i * ystep;
539 ymap[i] = val >> 16;
540 yoff[i] = ((255 - ((val >> 8) & 0xff)) * ypix_dist)>>8;
543 /* Interpolate */
544 for (y = 0; y < dst->h; y++) {
545 for (x = 0; x < dst->w; x++) {
546 uint32_t r, g, b;
547 uint32_t r1, g1, b1;
548 uint32_t x0, y0;
550 x0 = xmap[x];
551 y0 = ymap[y];
553 linear_lp_sample_x(src, x0, y0,
554 xpix_dist, xoff[x],
555 &r, &g, &b);
557 r = (r * yoff[y]) >> 14;
558 g = (g * yoff[y]) >> 14;
559 b = (b * yoff[y]) >> 14;
561 for (j = (1<<14) - yoff[y]; j > ypix_dist; j -= ypix_dist) {
563 x0 = xmap[x];
565 if (y0 < src->h - 1)
566 y0++;
568 linear_lp_sample_x(src, x0, y0,
569 xpix_dist, xoff[x],
570 &r1, &g1, &b1);
572 r += (r1 * ypix_dist) >> 14;
573 g += (g1 * ypix_dist) >> 14;
574 b += (b1 * ypix_dist) >> 14;
577 if (j > 0) {
578 x0 = xmap[x];
580 if (y0 < src->h - 1)
581 y0++;
583 linear_lp_sample_x(src, x0, y0,
584 xpix_dist, xoff[x],
585 &r1, &g1, &b1);
587 r += (r1 * j) >> 14;
588 g += (g1 * j) >> 14;
589 b += (b1 * j) >> 14;
592 r = (r + (1<<4))>>5;
593 g = (g + (1<<4))>>5;
594 b = (b + (1<<4))>>5;
596 GP_PutPixel_Raw_24BPP(dst, x, y,
597 GP_Pixel_CREATE_RGB888(r, g, b));
600 if (GP_ProgressCallbackReport(callback, y, dst->h, dst->w))
601 return 1;
604 GP_ProgressCallbackDone(callback);
605 return 0;
608 int GP_FilterInterpolate_LinearInt(const GP_Context *src, GP_Context *dst,
609 GP_ProgressCallback *callback)
611 uint32_t xmap[dst->w + 1];
612 uint32_t ymap[dst->h + 1];
613 uint8_t xoff[dst->w + 1];
614 uint8_t yoff[dst->h + 1];
615 uint32_t x, y, i;
617 GP_DEBUG(1, "Scaling image %ux%u -> %ux%u %2.2f %2.2f",
618 src->w, src->h, dst->w, dst->h,
619 1.00 * dst->w / src->w, 1.00 * dst->h / src->h);
621 /* Pre-compute mapping for interpolation */
622 uint32_t xstep = (src->w << 16) / dst->w;
624 for (i = 0; i < dst->w + 1; i++) {
625 uint32_t val = i * xstep;
626 xmap[i] = val >> 16;
627 xoff[i] = (val >> 8) & 0xff;
630 uint32_t ystep = (src->h << 16) / dst->h;
632 for (i = 0; i < dst->h + 1; i++) {
633 uint32_t val = i * ystep;
634 ymap[i] = val >> 16;
635 yoff[i] = (val >> 8) & 0xff;
638 /* Interpolate */
639 for (y = 0; y < dst->h; y++) {
640 for (x = 0; x < dst->w; x++) {
641 GP_Pixel pix00, pix01, pix10, pix11;
642 uint32_t r0, r1, g0, g1, b0, b1;
643 GP_Coord x0, x1, y0, y1;
645 x0 = xmap[x];
646 x1 = xmap[x] + 1;
648 if (x1 >= (GP_Coord)src->w)
649 x1 = src->w - 1;
651 y0 = ymap[y];
652 y1 = ymap[y] + 1;
654 if (y1 >= (GP_Coord)src->h)
655 y1 = src->h - 1;
657 pix00 = GP_GetPixel_Raw_24BPP(src, x0, y0);
658 pix10 = GP_GetPixel_Raw_24BPP(src, x1, y0);
659 pix01 = GP_GetPixel_Raw_24BPP(src, x0, y1);
660 pix11 = GP_GetPixel_Raw_24BPP(src, x1, y1);
662 r0 = GP_Pixel_GET_R_RGB888(pix00) * (255 - xoff[x]);
663 g0 = GP_Pixel_GET_G_RGB888(pix00) * (255 - xoff[x]);
664 b0 = GP_Pixel_GET_B_RGB888(pix00) * (255 - xoff[x]);
666 r0 += GP_Pixel_GET_R_RGB888(pix10) * xoff[x];
667 g0 += GP_Pixel_GET_G_RGB888(pix10) * xoff[x];
668 b0 += GP_Pixel_GET_B_RGB888(pix10) * xoff[x];
670 r1 = GP_Pixel_GET_R_RGB888(pix01) * (255 - xoff[x]);
671 g1 = GP_Pixel_GET_G_RGB888(pix01) * (255 - xoff[x]);
672 b1 = GP_Pixel_GET_B_RGB888(pix01) * (255 - xoff[x]);
674 r1 += GP_Pixel_GET_R_RGB888(pix11) * xoff[x];
675 g1 += GP_Pixel_GET_G_RGB888(pix11) * xoff[x];
676 b1 += GP_Pixel_GET_B_RGB888(pix11) * xoff[x];
678 r0 = (r1 * yoff[y] + r0 * (255 - yoff[y]) + (1<<15)) >> 16;
679 g0 = (g1 * yoff[y] + g0 * (255 - yoff[y]) + (1<<15)) >> 16;
680 b0 = (b1 * yoff[y] + b0 * (255 - yoff[y]) + (1<<15)) >> 16;
682 GP_PutPixel_Raw_24BPP(dst, x, y,
683 GP_Pixel_CREATE_RGB888(r0, g0, b0));
686 if (GP_ProgressCallbackReport(callback, y, dst->h, dst->w))
687 return 1;
690 GP_ProgressCallbackDone(callback);
691 return 0;
694 int GP_FilterInterpolate_LinearLFInt(const GP_Context *src, GP_Context *dst,
695 GP_ProgressCallback *callback)
697 float x_rat = 1.00 * dst->w / src->w;
698 float y_rat = 1.00 * dst->h / src->h;
700 if (x_rat < 1.00 && y_rat < 1.00) {
701 GP_DEBUG(1, "Downscaling image %ux%u -> %ux%u %2.2f %2.2f",
702 src->w, src->h, dst->w, dst->h, x_rat, y_rat);
703 return interpolate_linear_lp_xy(src, dst, callback);
706 //TODO: x_rat > 1.00 && y_rat < 1.00
707 //TODO: x_rat < 1.00 && y_rat > 1.00
709 return GP_FilterInterpolate_LinearInt(src, dst, callback);
712 int GP_FilterResize_Raw(const GP_Context *src, GP_Context *dst,
713 GP_InterpolationType type,
714 GP_ProgressCallback *callback)
716 switch (type) {
717 case GP_INTERP_NN:
718 return GP_FilterResizeNN_Raw(src, dst, callback);
719 case GP_INTERP_LINEAR_INT:
720 return GP_FilterInterpolate_LinearInt(src, dst, callback);
721 case GP_INTERP_LINEAR_LF_INT:
722 return GP_FilterInterpolate_LinearLFInt(src, dst, callback);
723 case GP_INTERP_CUBIC:
724 return GP_FilterInterpolate_Cubic(src, dst, callback);
725 case GP_INTERP_CUBIC_INT:
726 return GP_FilterInterpolate_CubicInt(src, dst, callback);
729 return 1;
732 GP_Context *GP_FilterResize(const GP_Context *src, GP_Context *dst,
733 GP_InterpolationType type,
734 GP_Size w, GP_Size h,
735 GP_ProgressCallback *callback)
737 GP_Context sub, *res;
739 /* TODO: Templatize */
740 if (src->pixel_type != GP_PIXEL_RGB888)
741 return NULL;
743 if (dst == NULL) {
744 res = GP_ContextAlloc(w, h, src->pixel_type);
746 if (res == NULL)
747 return NULL;
748 } else {
749 GP_ASSERT(src->pixel_type == dst->pixel_type,
750 "The src and dst pixel types must match");
752 * The size of w and h is asserted in subcontext initalization
754 res = GP_SubContext(dst, &sub, 0, 0, w, h);
758 * Operation was aborted by progress callback.
760 * Free any alloacted data and exit.
762 if (GP_FilterResize_Raw(src, res, type, callback)) {
763 GP_DEBUG(1, "Operation aborted");
765 if (dst == NULL)
766 GP_ContextFree(dst);
768 return NULL;
771 return dst == NULL ? res : dst;