summaryrefslogtreecommitdiffstats
path: root/debian/transcode/transcode-1.1.7/libtcvideo/zoom.c
diff options
context:
space:
mode:
Diffstat (limited to 'debian/transcode/transcode-1.1.7/libtcvideo/zoom.c')
-rw-r--r--debian/transcode/transcode-1.1.7/libtcvideo/zoom.c698
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:
+ */