diff options
Diffstat (limited to 'debian/transcode/transcode-1.1.7/libtcvideo/zoom.c')
| -rw-r--r-- | debian/transcode/transcode-1.1.7/libtcvideo/zoom.c | 698 |
1 files changed, 698 insertions, 0 deletions
diff --git a/debian/transcode/transcode-1.1.7/libtcvideo/zoom.c b/debian/transcode/transcode-1.1.7/libtcvideo/zoom.c new file mode 100644 index 00000000..3a6d1410 --- /dev/null +++ b/debian/transcode/transcode-1.1.7/libtcvideo/zoom.c @@ -0,0 +1,698 @@ +/* + * zoom.c - resize an image to an arbitrary size with filtering + * Based on "Filtered Image Rescaling" by Dale Schumacher (public domain). + * + * This file is part of transcode, a video stream processing tool. + * transcode is free software, distributable under the terms of the GNU + * General Public License (version 2 or later). See the file COPYING + * for details. + */ + +#include "tcvideo.h" +#include "zoom.h" + +#include "src/transcode.h" +#include <math.h> + +/*************************************************************************/ + +/* Data structures for holding resizing data (used internally). */ + +/* A contributor to a single pixel */ +struct contrib { + int pixel; + double weight; +}; + +/* List of all contributors to a single pixel */ +struct clist { + int n; /* Number of contributors */ + struct contrib *list; /* Pointer to list of contributors */ +}; + +/* Data for a resize operation */ +struct zoominfo { + int old_w, old_h; /* Original width and height */ + int new_w, new_h; /* New width and height */ + int Bpp; /* Bytes per pixel */ + int old_stride; /* Bytes per line (original image) */ + int new_stride; /* Bytes per line (new image) */ + double (*filter)(double); /* Filter function */ + double fwidth; /* Filter width */ + int32_t *x_contrib; /* Contributors in the horizontal direction */ + int32_t *y_contrib; /* Contributors in the vertical direction */ + uint8_t *tmpimage; /* Temporary buffer */ +}; + +/* Convert a double to a 16.16 fixed-point value */ +#define DOUBLE_TO_FIXED(v) ((int32_t)((v)*65536)) + +/* Convert a 16.16 fixed-point value to an integer */ +#define FIXED_TO_INT(v) ((v)>>16) + +/*************************************************************************/ + +/* FIXME: use a static table for every data related to a filter, + * accessed by filter_id; + * typedef struct tcvzoomdata_ TCVZoomData; + * struct tcvzoom_data { + * const char *name; + * double support; + * double (*filter)(double t); + * }; + */ + +/** + * tcv_zoom_filter_to_string: + * return the human-readable name of a given filter, as string. + * + * Parameters: + * filter: Filter identifier (TCV_ZOOM_*). + * Return value: + * name of the given filter. DO NOT free() it. + * NULL if the filter is unknown/unsupported. + */ +const char *tcv_zoom_filter_to_string(TCVZoomFilter filter) +{ + const char *name = NULL; + if (filter == TCV_ZOOM_BELL) { + name = "Bell"; + } else if (filter == TCV_ZOOM_BOX) { + name = "Box"; + } else if (filter == TCV_ZOOM_B_SPLINE) { + name = "B_spline"; + } else if (filter == TCV_ZOOM_HERMITE) { + name = "Hermite"; + } else if (filter == TCV_ZOOM_LANCZOS3) { + name = "Lanczos3"; + } else if (filter == TCV_ZOOM_MITCHELL) { + name = "Mitchell"; + } else if (filter == TCV_ZOOM_TRIANGLE) { + name = "Triangle"; + } else if (filter == TCV_ZOOM_CUBIC_KEYS4) { + name = "Cubic_Keys4"; + } else if (filter == TCV_ZOOM_SINC8) { + name = "Sinc8"; + } else if (filter == TCV_ZOOM_DEFAULT) { + name = "Lanczos3"; + } else { + name = NULL; + } + return name; +} + +/** + * tcv_zoom_filter_from_string: + * return the filter id given its human-readable name, as string. + * case insensitive. + * + * Parameters: + * name: name fo the given filter. + * Return value: + * the corrisponding identifier of the given name. + * TCV_ZOOM_NULL if the filter is unknown/unsupported. + */ +TCVZoomFilter tcv_zoom_filter_from_string(const char *name) +{ + TCVZoomFilter filter = TCV_ZOOM_NULL; + if (strcasecmp(name, "bell") == 0) { + filter = TCV_ZOOM_BELL; + } else if (strcasecmp(name, "box") == 0) { + filter = TCV_ZOOM_BOX; + } else if (strcasecmp(name, "b_spline") == 0) { + filter = TCV_ZOOM_B_SPLINE; + } else if (strcasecmp(name, "hermite") == 0) { + filter = TCV_ZOOM_HERMITE; + } else if (strcasecmp(name, "lanczos3") == 0) { + filter = TCV_ZOOM_LANCZOS3; + } else if (strcasecmp(name, "mitchell") == 0) { + filter = TCV_ZOOM_MITCHELL; + } else if (strcasecmp(name, "triangle") == 0) { + filter = TCV_ZOOM_TRIANGLE; + } else if (strcasecmp(name, "cubic_keys4") == 0) { + filter = TCV_ZOOM_CUBIC_KEYS4; + } else if (strcasecmp(name, "sinc8") == 0) { + filter = TCV_ZOOM_SINC8; + } else if (strcasecmp(name, "default") == 0) { + filter = TCV_ZOOM_LANCZOS3; + } else { + filter = TCV_ZOOM_NULL; + } + return filter; +} + +/*************************************************************************/ +/*************************************************************************/ + +/** + * *_filter: Filter functions for resizing. + * + * Parameters: + * t: Filter parameter. + * Return value: + * Filter result. + * Postconditions: 0 <= t && t <= 1 + */ + +/************************************/ + +static const double hermite_support = 1.0; + +static double hermite_filter(double t) +{ + /* f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1 */ + if (t < 0.0) + t = -t; + if (t < 1.0) + return (2.0 * t - 3.0) * t * t + 1.0; + return 0.0; +} + +/************************************/ + +static const double box_support = 0.5; + +static double box_filter(double t) +{ + if ((t > -0.5) && (t <= 0.5)) + return 1.0; + return 0.0; +} + +/************************************/ + +static const double triangle_support = 1.0; + +static double triangle_filter(double t) +{ + if (t < 0.0) + t = -t; + if (t < 1.0) + return 1.0 - t; + return 0.0; +} + +/************************************/ + +static const double bell_support = 1.5; + +static double bell_filter(double t) +{ + if (t < 0) + t = -t; + if (t < .5) + return .75 - (t * t); + if (t < 1.5) { + t = (t - 1.5); + return .5 * (t * t); + } + return 0.0; +} + +/************************************/ + +static const double B_spline_support = 2.0; + +static double B_spline_filter(double t) +{ + double tt; + + if (t < 0) + t = -t; + if (t < 1) { + tt = t * t; + return (.5 * tt * t) - tt + (2.0 / 3.0); + } else if (t < 2) { + t = 2 - t; + return (1.0 / 6.0) * (t * t * t); + } + return 0.0; +} + +/************************************/ + +static const double lanczos3_support = 3.0; + +#ifndef PI +# define PI 3.14159265358979323846264338327950 +#endif +#define SINC(x) ((x) != 0 ? sin((x)*PI) / ((x)*PI) : 1.0) + +static double lanczos3_filter(double t) +{ + if (t < 0) + t = -t; + if (t < 3.0) + return SINC(t) * SINC(t/3.0); + return 0.0; +} + +#undef SINC + +/************************************/ + +static const double mitchell_support = 2.0; + +#define B (1.0 / 3.0) +#define C (1.0 / 3.0) + +static double mitchell_filter(double t) +{ + double tt; + + tt = t * t; + if (t < 0) + t = -t; + if (t < 1.0) { + t = (((12.0 - 9.0 * B - 6.0 * C) * (t * tt)) + + ((-18.0 + 12.0 * B + 6.0 * C) * tt) + + (6.0 - 2 * B)); + return t / 6.0; + } else if (t < 2.0) { + t = (((-1.0 * B - 6.0 * C) * (t * tt)) + + ((6.0 * B + 30.0 * C) * tt) + + ((-12.0 * B - 48.0 * C) * t) + + (8.0 * B + 24 * C)); + return t / 6.0; + } + return 0.0; +} + +#undef B +#undef C + +/************************************/ +/* Keys 4th-order Cubic */ + +static const double cubic_keys4_support = 3.0; + +static double cubic_keys4_filter(double t) +{ + if (t < 0.0) + t = -t; + if (t < 1.0) + return (3.0 + (t * t * (-7.0 + (t * 4.0)))) / 3.0; + if (t < 2.0) + return (30.0 + (t * (-59.0 + (t * (36.0 + (t * -7.0)))))) / 12.0; + if (t < 3.0) + return (-18.0 + (t * (21.0 + (t * (-8.0 + t))))) / 12.0; + return 0.0; +} + +/************************************/ +/* Sinc with Lanczos window, 8 cycles */ + +static const double sinc8_support = 8.0; + +static double sinc8_filter(double t) +{ + if (t < 0.0) + t = -t; + if (t == 0.0) { + return 1.0; + } else if (t < 8.0) { + double w = sin(PI*t / 8.0) / (PI*t / 8.0); + return w * sin(t*PI) / (t*PI); + } else { + return 0.0; + } +} + +/*************************************************************************/ + +/** + * gen_contrib: Helper function to generate the list of contributors to + * each resized pixel in one direction (horizontal or vertical). + * + * Parameters: + * oldsize: Size of original image in the direction for which + * contributors are being generated. + * newsize: Size of resized image in the direction for which + * contributors are being generated. + * stride: Number of bytes between adjacent pixels in the direction + * for which contributors are being generated. + * filter: As for zoom_process(). + * fwidth: As for zoom_process(). + * Return value: + * A pointer to a `newsize'-element array of `struct clist' elements, + * or NULL on error (out of memory). + * Preconditions: + * oldsize > 0 + * newsize > 0 + * stride > 0 + * filter != NULL + * fwidth > 0 + */ + +static struct clist *gen_contrib(int oldsize, int newsize, int stride, + double (*filter)(double), double fwidth) +{ + struct clist *contrib; + double scale = (double)newsize / (double)oldsize; + double new_fwidth, fscale; + int i, j; + + contrib = tc_zalloc(newsize * sizeof(struct clist)); + + if (scale < 1.0) { + fscale = 1.0 / scale; + } else { + fscale = 1.0; + } + new_fwidth = fwidth * fscale; + for (i = 0; i < newsize; ++i) { + double center = (double) i / scale; + int left = ceil(center - new_fwidth); + int right = floor(center + new_fwidth); + contrib[i].n = 0; + contrib[i].list = tc_zalloc((right-left+1) * sizeof(struct contrib)); + for (j = left; j <= right; ++j) { + int k, n; + double weight = center - (double) j; + weight = (*filter)(weight / fscale) / fscale; + if (j < 0) { + n = -j; + } else if (j >= oldsize) { + n = (oldsize - j) + oldsize - 1; + } else { + n = j; + } + k = contrib[i].n++; + contrib[i].list[k].pixel = n*stride; + contrib[i].list[k].weight = weight; + } + } + + return contrib; +} + +/*************************************************************************/ +/*************************************************************************/ + +/* External interface. */ + +/*************************************************************************/ + +/** + * zoom_init: Allocate, initialize, and return a ZoomInfo structure for + * use in subsequent zoom_process() calls. The structure should be freed + * with zoom_free() when no longer needed. + * + * Parameters: + * old_w: Width of original image. + * old_h: Height of original image. + * new_w: Width of resized image. + * new_h: Height of resized image. + * Bpp: Bytes (not bits!) per pixel. + * old_stride: Bytes per line of original image. + * new_stride: Bytes per line of resized image. + * filter: Filter identifier (TCV_ZOOM_*). + * Return value: + * A pointer to a newly allocated ZoomInfo structure, or NULL on error + * (invalid parameters or out of memory). + */ + +ZoomInfo *zoom_init(int old_w, int old_h, int new_w, int new_h, int Bpp, + int old_stride, int new_stride, TCVZoomFilter filter) +{ + ZoomInfo *zi; + struct clist *x_contrib = NULL, *y_contrib = NULL; + + /* Sanity check */ + if (old_w <= 0 || old_h <= 0 || new_w <= 0 || new_h <= 0 || Bpp <= 0 + || old_stride <= 0 || new_stride <= 0) + return NULL; + + /* Allocate structure */ + zi = tc_malloc(sizeof(*zi)); + if (!zi) + return NULL; + + /* Set up scalar members, and check filter value */ + zi->old_w = old_w; + zi->old_h = old_h; + zi->new_w = new_w; + zi->new_h = new_h; + zi->Bpp = Bpp; + zi->old_stride = old_stride; + zi->new_stride = new_stride; + switch (filter) { + case TCV_ZOOM_BOX: + zi->filter = box_filter; + zi->fwidth = box_support; + break; + case TCV_ZOOM_TRIANGLE: + zi->filter = triangle_filter; + zi->fwidth = triangle_support; + break; + case TCV_ZOOM_HERMITE: + zi->filter = hermite_filter; + zi->fwidth = hermite_support; + break; + case TCV_ZOOM_BELL: + zi->filter = bell_filter; + zi->fwidth = bell_support; + break; + case TCV_ZOOM_B_SPLINE: + zi->filter = B_spline_filter; + zi->fwidth = B_spline_support; + break; + case TCV_ZOOM_MITCHELL: + zi->filter = mitchell_filter; + zi->fwidth = mitchell_support; + break; + case TCV_ZOOM_LANCZOS3: + zi->filter = lanczos3_filter; + zi->fwidth = lanczos3_support; + break; + case TCV_ZOOM_CUBIC_KEYS4: + zi->filter = cubic_keys4_filter; + zi->fwidth = cubic_keys4_support; + break; + case TCV_ZOOM_SINC8: + zi->filter = sinc8_filter; + zi->fwidth = sinc8_support; + break; + default: + free(zi); + return NULL; + } + + /* Generate contributor lists and allocate temporary image buffer */ + zi->x_contrib = NULL; + zi->y_contrib = NULL; + zi->tmpimage = tc_malloc(new_w * old_h * Bpp); + if (!zi->tmpimage) + goto error_out; + if (old_w != new_w) { + x_contrib = gen_contrib(old_w, new_w, Bpp, zi->filter, zi->fwidth); + if (!x_contrib) + goto error_out; + } + if (old_h != new_h) { + /* Calculate the correct stride--if the width isn't changing, + * this will just be old_stride */ + int stride = (old_w==new_w) ? old_stride : Bpp*new_w; + y_contrib = gen_contrib(old_h, new_h, stride, zi->filter, + zi->fwidth); + if (!y_contrib) + goto error_out; + } + + /* Convert contributor lists into flat arrays and fixed-point values. + * The flat array consists of a contributor count plus two values per + * contributor (index and fixed-point weight) for each output pixel. + * Note that for the horizontal direction, we make `Bpp' copies of the + * contributors, adjusting the offset for each byte of the pixel. */ + + if (x_contrib) { + int count = 0, i; + int32_t *ptr; + + for (i = 0; i < new_w; i++) + count += 1 + 2 * x_contrib[i].n; + zi->x_contrib = tc_malloc(sizeof(int32_t) * count * Bpp); + if (!zi->x_contrib) + goto error_out; + for (ptr = zi->x_contrib, i = 0; i < new_w * Bpp; i++) { + int j; + *ptr++ = x_contrib[i/Bpp].n; + for (j = 0; j < x_contrib[i/Bpp].n; j++) { + *ptr++ = x_contrib[i/Bpp].list[j].pixel + i%Bpp; + *ptr++ = DOUBLE_TO_FIXED(x_contrib[i/Bpp].list[j].weight); + } + } + /* Free original contributor list */ + for (i = 0; i < new_w; i++) + free(x_contrib[i].list); + free(x_contrib); + x_contrib = NULL; + } + + if (y_contrib) { + int count = 0, i; + int32_t *ptr; + + for (i = 0; i < new_h; i++) + count += 1 + 2 * y_contrib[i].n; + zi->y_contrib = tc_malloc(sizeof(int32_t) * count); + if (!zi->y_contrib) + goto error_out; + for (ptr = zi->y_contrib, i = 0; i < new_h; i++) { + int j; + *ptr++ = y_contrib[i].n; + for (j = 0; j < y_contrib[i].n; j++) { + *ptr++ = y_contrib[i].list[j].pixel; + *ptr++ = DOUBLE_TO_FIXED(y_contrib[i].list[j].weight); + } + } + for (i = 0; i < new_h; i++) + free(y_contrib[i].list); + free(y_contrib); + y_contrib = NULL; + } + + /* Done */ + return zi; + + error_out: + { + if (x_contrib) { + int i; + for (i = 0; i < new_w; i++) + free(x_contrib[i].list); + free(x_contrib); + } + if (y_contrib) { + int i; + for (i = 0; i < new_w; i++) + free(x_contrib[i].list); + free(x_contrib); + } + zoom_free(zi); + return NULL; + } +} + +/*************************************************************************/ + +/** + * zoom_process: Image resizing core. + * + * Parameters: + * zi: ZoomInfo structure allocated by zoom_init(). + * src: Source data plane. + * dest: Destination data plane. + * Return value: None. + * Preconditions: + * zi was allocated by zoom_init() + * src != NULL + * dest != NULL + * src and dest do not overlap + */ + +/* clamp the input to the specified range */ +#define CLAMP(v,l,h) ((v)<(l) ? (l) : (v) > (h) ? (h) : (v)) + +void zoom_process(const ZoomInfo *zi, const uint8_t *src, uint8_t *dest) +{ + int from_stride, to_stride; + const uint8_t *from; + uint8_t *to; + + from = src; + from_stride = zi->old_stride; + + /* Apply filter to zoom horizontally from src to tmp (if necessary) */ + if (zi->x_contrib) { + int y; + to = zi->tmpimage; + to_stride = zi->new_w * zi->Bpp; + for (y = 0; y < zi->old_h; y++, from += from_stride, to += to_stride) { + int32_t *contrib = zi->x_contrib; + int x; + for (x = 0; x < zi->new_w * zi->Bpp; x++) { + int32_t weight = DOUBLE_TO_FIXED(0.5); + int n = *contrib++, i; + for (i = 0; i < n; i++) { + int pixel = *contrib++; + weight += from[pixel] * (*contrib++); + } + to[x] = CLAMP(FIXED_TO_INT(weight), 0, 255); + } + } + from = zi->tmpimage; + from_stride = to_stride; + } + + /* Apply filter to zoom vertically from tmp (or src) to dest */ + /* Use Y as the outside loop to avoid cache thrashing on output buffer */ + to = dest; + to_stride = zi->new_stride; + if (zi->y_contrib) { + int32_t *contrib = zi->y_contrib; + int y; + for (y = 0; y < zi->new_h; y++, to += to_stride) { + int n = *contrib++, x; + for (x = 0; x < zi->new_w * zi->Bpp; x++) { + int32_t weight = DOUBLE_TO_FIXED(0.5); + int i; + for (i = 0; i < n; i++) { + int pixel = contrib[i*2]; + weight += from[x+pixel] * contrib[i*2+1]; + } + to[x] = CLAMP(FIXED_TO_INT(weight), 0, 255); + } + contrib += 2*n; + } + } else { + /* No zooming necessary, just copy */ + if (from_stride == zi->new_w*zi->Bpp + && to_stride == zi->new_w*zi->Bpp + ) { + /* We can copy the whole frame at once */ + ac_memcpy(to, from, to_stride * zi->new_h); + } else { + /* Copy one row at a time */ + int y; + for (y = 0; y < zi->new_h; y++) { + ac_memcpy(to + y*to_stride, from + y*from_stride, + zi->new_w * zi->Bpp); + } + } + } +} + +/*************************************************************************/ + +/** + * zoom_free(): Free a ZoomInfo structure. + * + * Parameters: + * zi: ZoomInfo structure allocated by zoom_init(). + * Return value: + * None. + * Preconditions: + * zi was allocated by zoom_init() + * Postconditions: + * zi is freed + */ +void zoom_free(ZoomInfo *zi) +{ + free(zi->x_contrib); + free(zi->y_contrib); + free(zi->tmpimage); + free(zi); +} + +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ |
