1 /********* FIXME: tcvhandle sanity checks ************/
2 /*
3  * tcvideo.c - video processing library for transcode
4  * Written by Andrew Church <achurch@achurch.org>
5  * Based on code written by Thomas Oestreich.
6  *
7  * This file is part of transcode, a video stream processing tool.
8  * transcode is free software, distributable under the terms of the GNU
9  * General Public License (version 2 or later).  See the file COPYING
10  * for details.
11  */
12 
13 #include "tcvideo.h"
14 #include "zoom.h"
15 
16 #define zoom zoom_  // temp to avoid name conflict
17 #include "src/transcode.h"
18 #undef zoom
19 #include <math.h>
20 
21 /*************************************************************************/
22 
23 #ifndef PI
24 # ifdef M_PI
25 #  define PI M_PI
26 # else
27 #  define PI 3.14159265358979323846264338327950
28 # endif
29 #endif
30 
31 /* Antialiasing threshold for determining whether two pixels are the same
32  * color. */
33 #define AA_DIFFERENT 25
34 
35 /* Data for generating a resized pixel. */
36 struct resize_table_elem {
37     int source;
38     uint32_t weight1, weight2;
39 };
40 
41 /* Maximum number of ZoomInfo structures to cache. */
42 #define ZOOMINFO_CACHE_SIZE 10
43 
44 
45 /* Internal data structure to hold various state information.  The
46  * TCVHandle returned by tcv_init() and passed by the caller to other
47  * functions is a pointer to this structure. */
48 
49 struct tcvhandle_ {
50     /* Various lookup tables */
51     struct resize_table_elem resize_table_x[TC_MAX_V_FRAME_WIDTH/8];
52     struct resize_table_elem resize_table_y[TC_MAX_V_FRAME_HEIGHT/8];
53     uint8_t gamma_table[256];
54     uint32_t aa_table_c[256];
55     uint32_t aa_table_x[256];
56     uint32_t aa_table_y[256];
57     uint32_t aa_table_d[256];
58     /* Initialization values used in creating lookup tables (so we know
59      * whether we have to re-create them */
60     int saved_oldw, saved_neww, saved_oldh, saved_newh;
61     double saved_gamma;
62     double saved_weight, saved_bias;
63     /* ZoomInfo cache */
64     struct {
65         int old_w, old_h, new_w, new_h, Bpp, ilace;
66         TCVZoomFilter filter;
67         ZoomInfo *zi;
68     } zoominfo_cache[ZOOMINFO_CACHE_SIZE];
69     /* Buffer and buffer size for tcv_convert() */
70     uint8_t *convert_buffer;
71     uint32_t convert_buffer_size;
72 };
73 
74 /*************************************************************************/
75 
76 /* Internal-use functions (defined at the bottom of the file). */
77 
78 static void init_resize_tables(TCVHandle handle,
79                                int oldw, int neww, int oldh, int newh);
80 static void init_one_resize_table(struct resize_table_elem *table,
81                                   int oldsize, int newsize);
82 static void init_gamma_table(TCVHandle handle, double gamma);
83 static void init_aa_table(TCVHandle handle, double aa_weight, double aa_bias);
84 
85 /*************************************************************************/
86 /*************************************************************************/
87 
88 /* External interface functions. */
89 
90 /*************************************************************************/
91 
92 /**
93  * tcv_init:  Create and return a handle for use in other tcvideo
94  * functions.  The handle should be freed with tcv_free() when no longer
95  * needed.  Note that if you need to operate on multiple image sizes or
96  * use different gamma or antialiasing values, you will get improved
97  * performance by using separate handles for each set of values.  (However,
98  * tcv_zoom() can cache lookup tables for multiple sets of image sizes,
99  * currently 10 sets.)
100  *
101  * Parameters: None.
102  * Return value: A handle to be passed to other tcvideo functions, or 0 on
103  *               error.
104  * Preconditions: None.
105  * Postconditions: None.
106  */
107 
tcv_init(void)108 TCVHandle tcv_init(void)
109 {
110     TCVHandle handle;
111 
112     handle = tc_zalloc(sizeof(*handle));
113     if (handle) {
114         handle->saved_weight = handle->saved_bias = -1.0;
115     }
116     return handle;
117 }
118 
119 /*************************************************************************/
120 
121 /**
122  * tcv_free:  Free resources allocated for the given handle.  Does nothing
123  * if handle is zero.
124  *
125  * Parameters: handle: tcvideo handle.
126  * Return value: None.
127  * Preconditions: handle != 0: handle was returned by tcv_init()
128  * Postconditions: None.
129  */
130 
tcv_free(TCVHandle handle)131 void tcv_free(TCVHandle handle)
132 {
133     if (handle) {
134         int i;
135         for (i = 0; i < ZOOMINFO_CACHE_SIZE; i++) {
136             if (handle->zoominfo_cache[i].zi)
137                 zoom_free(handle->zoominfo_cache[i].zi);
138         }
139         free(handle);
140     }
141 }
142 
143 /*************************************************************************/
144 
145 /**
146  * tcv_clip:  Clip the given image by removing the specified number of
147  * pixels from each edge.  If a clip value is negative, instead expands the
148  * frame by inserting the given number of black pixels (the value to be
149  * inserted is given by the `black_pixel' parameter).  Conceptually,
150  * expansion is done before clipping, so that if, for example,
151  *     width == 640
152  *     clip_left == 642
153  *     clip_right == -4
154  * then the result is a two-pixel-wide black frame (this is not considered
155  * an error).
156  *
157  * Parameters:      handle: tcvideo handle.
158  *                     src: Source data plane.
159  *                    dest: Destination data plane.
160  *                   width: Width of frame.
161  *                  height: Height of frame.
162  *                     Bpp: Bytes (not bits!) per pixel.
163  *               clip_left: Number of pixels to clip from left edge.
164  *              clip_right: Number of pixels to clip from right edge.
165  *                clip_top: Number of pixels to clip from top edge.
166  *             clip_bottom: Number of pixels to clip from bottom edge.
167  *             black_pixel: Value to be filled into expanded areas.
168  * Return value: Nonzero on success, zero on error (invalid parameters).
169  * Preconditions: handle != 0: handle was returned by tcv_init()
170  *                src != NULL: src[0]..src[width*height*Bpp-1] are readable
171  *                dest != NULL:
172  *                    destw = width - clip_left - clip_right;
173  *                    desth = height - clip_top - clip_bottom;
174  *                    dest[0]..dest[destw*desth*Bpp-1] are writable
175  *                src != dest: src and dest do not overlap
176  * Postconditions: (on success) dest[0]..dest[destw*desth*Bpp-1] are set
177  */
178 
tcv_clip(TCVHandle handle,uint8_t * src,uint8_t * dest,int width,int height,int Bpp,int clip_left,int clip_right,int clip_top,int clip_bottom,uint8_t black_pixel)179 int tcv_clip(TCVHandle handle,
180              uint8_t *src, uint8_t *dest, int width, int height, int Bpp,
181              int clip_left, int clip_right, int clip_top, int clip_bottom,
182              uint8_t black_pixel)
183 {
184     int new_w, copy_w, copy_h, y;
185 
186 
187     if (!src || !dest || width <= 0 || height <= 0 || (Bpp != 1 && Bpp != 3)) {
188         tc_log_error("libtcvideo", "tcv_clip: invalid frame parameters!");
189         return 0;
190     }
191     if (clip_left + clip_right >= width || clip_top + clip_bottom >= height) {
192         tc_log_error("libtcvideo", "tcv_clip: clipping parameters"
193                      " (%d,%d,%d,%d) invalid for frame size %dx%d",
194                      clip_top, clip_left, clip_bottom, clip_right,
195                      width, height);
196         return 0;
197     }
198     /* Normalize clipping values (e.g. clip_left > width, clip_right < 0) */
199     if (clip_left > width) {
200         clip_right += clip_left - width;
201         clip_left = width;
202     }
203     if (clip_right > width) {
204         clip_left += clip_right - width;
205         clip_right = width;
206     }
207     if (clip_top > height) {
208         clip_bottom += clip_top - height;
209         clip_top = height;
210     }
211     if (clip_bottom > height) {
212         clip_top += clip_bottom - height;
213         clip_bottom = height;
214     }
215 
216     new_w = width - clip_left - clip_right;
217     copy_w = width - (clip_left<0 ? 0 : clip_left)
218                    - (clip_right<0 ? 0 : clip_right);
219     copy_h = height - (clip_top<0 ? 0 : clip_top)
220                     - (clip_bottom<0 ? 0 : clip_bottom);
221 
222     if (clip_top < 0) {
223         memset(dest, black_pixel, (-clip_top) * new_w * Bpp);
224         dest += (-clip_top) * new_w * Bpp;
225     } else {
226         src += clip_top * width * Bpp;
227     }
228     if (clip_left > 0)
229         src += clip_left * Bpp;
230     for (y = 0; y < copy_h; y++) {
231         if (clip_left < 0) {
232             memset(dest, black_pixel, (-clip_left) * Bpp);
233             dest += (-clip_left) * Bpp;
234         }
235         if (copy_w > 0)
236             ac_memcpy(dest, src, copy_w * Bpp);
237         dest += copy_w * Bpp;
238         src += width * Bpp;
239         if (clip_right < 0) {
240             memset(dest, black_pixel, (-clip_right) * Bpp);
241             dest += (-clip_right) * Bpp;
242         }
243     }
244     if (clip_bottom < 0) {
245         memset(dest, black_pixel, (-clip_bottom) * new_w * Bpp);
246     }
247     return 1;
248 }
249 
250 /*************************************************************************/
251 
252 /**
253  * tcv_deinterlace:  Deinterlace the given image.
254  *
255  * Parameters: handle: tcvideo handle.
256  *                src: Source data plane.
257  *               dest: Destination data plane.
258  *              width: Width of frame.
259  *             height: Height of frame.
260  *                Bpp: Bytes (not bits!) per pixel.
261  *               mode: Deinterlacing mode (TCV_DEINTERLACE_* constant).
262  * Return value: Nonzero on success, zero on error (invalid parameters).
263  * Preconditions: handle != 0: handle was returned by tcv_init()
264  *                src != NULL: src[0]..src[width*height*Bpp-1] are readable
265  *                dest != NULL:
266  *                    mode == TCV_DEINTERLACE_DROP_FIELD_{TOP,BOTTOM}:
267  *                        dest[0]..dest[width*(height/2)*Bpp-1] are writable
268  *                    mode != TCV_DEINTERLACE_DROP_FIELD_{TOP,BOTTOM}:
269  *                        dest[0]..dest[width*height*Bpp-1] are writable
270  *                src != dest: src and dest do not overlap
271  * Postconditions: (on success)
272  *                     mode == TCV_DEINTERLACE_DROP_FIELD_{TOP,BOTTOM}:
273  *                         dest[0]..dest[width*(height/2)*Bpp-1] are set
274  *                     mode != TCV_DEINTERLACE_DROP_FIELD_{TOP,BOTTOM}:
275  *                         dest[0]..dest[width*height*Bpp-1] are set
276  */
277 
278 static int deint_drop_field(uint8_t *src, uint8_t *dest, int width,
279                             int height, int Bpp, int drop_top);
280 static int deint_interpolate(uint8_t *src, uint8_t *dest, int width,
281                              int height, int Bpp);
282 static int deint_linear_blend(uint8_t *src, uint8_t *dest, int width,
283                               int height, int Bpp);
284 
tcv_deinterlace(TCVHandle handle,uint8_t * src,uint8_t * dest,int width,int height,int Bpp,TCVDeinterlaceMode mode)285 int tcv_deinterlace(TCVHandle handle,
286                     uint8_t *src, uint8_t *dest, int width, int height,
287                     int Bpp, TCVDeinterlaceMode mode)
288 {
289     if (!src || !dest || width <= 0 || height <= 0 || (Bpp != 1 && Bpp != 3)) {
290         tc_log_error("libtcvideo", "tcv_deinterlace: invalid frame parameters!");
291         return 0;
292     }
293     switch (mode) {
294       case TCV_DEINTERLACE_DROP_FIELD_TOP:
295         return deint_drop_field(src, dest, width, height, Bpp, 1);
296       case TCV_DEINTERLACE_DROP_FIELD_BOTTOM:
297         return deint_drop_field(src, dest, width, height, Bpp, 0);
298       case TCV_DEINTERLACE_INTERPOLATE:
299         return deint_interpolate(src, dest, width, height, Bpp);
300       case TCV_DEINTERLACE_LINEAR_BLEND:
301         return deint_linear_blend(src, dest, width, height, Bpp);
302       default:
303         tc_log_error("libtcvideo", "tcv_deinterlace: invalid mode %d!", mode);
304         return 0;
305     }
306 }
307 
308 /**
309  * deint_drop_field, deint_interpolate, deint_linear_blend:  Helper
310  * functions for tcv_deinterlace() that implement the individual
311  * deinterlacing methods.
312  *
313  * Parameters: As for tcv_deinterlace(), less `handle'.
314  * Return value: As for tcv_deinterlace().
315  * Side effects: (for deint_linear_blend())
316  *                   src[0..width*height-1] are destroyed.
317  * Preconditions: As for tcv_deinterlace(), less `handle', plus:
318  *                src != NULL
319  *                dest != NULL
320  *                width > 0
321  *                height > 0
322  *                Bpp == 1 || Bpp == 3
323  *                (for deint_linear_blend())
324  *                    src[0..width*height-1] are writable
325  * Postconditions: As for tcv_deinterlace().
326  */
327 
deint_drop_field(uint8_t * src,uint8_t * dest,int width,int height,int Bpp,int drop_top)328 static int deint_drop_field(uint8_t *src, uint8_t *dest, int width,
329                             int height, int Bpp, int drop_top)
330 {
331     int Bpl = width * Bpp;
332     int y;
333 
334     if (drop_top)
335         src += Bpl;
336     for (y = 0; y < height/2; y++)
337         ac_memcpy(dest + y*Bpl, src + (y*2)*Bpl, Bpl);
338     return 1;
339 }
340 
341 
deint_interpolate(uint8_t * src,uint8_t * dest,int width,int height,int Bpp)342 static int deint_interpolate(uint8_t *src, uint8_t *dest, int width,
343                              int height, int Bpp)
344 {
345     int Bpl = width * Bpp;
346     int y;
347 
348     for (y = 0; y < height; y++) {
349         if (y%2 == 0) {
350             ac_memcpy(dest + y*Bpl, src + y*Bpl, Bpl);
351         } else if (y == height-1) {
352             /* if the last line is odd, copy from the previous line */
353             ac_memcpy(dest + y*Bpl, src + (y-1)*Bpl, Bpl);
354         } else {
355             ac_average(src + (y-1)*Bpl, src + (y+1)*Bpl, dest + y*Bpl, Bpl);
356         }
357     }
358     return 1;
359 }
360 
361 
deint_linear_blend(uint8_t * src,uint8_t * dest,int width,int height,int Bpp)362 static int deint_linear_blend(uint8_t *src, uint8_t *dest, int width,
363                               int height, int Bpp)
364 {
365     int Bpl = width * Bpp;
366     int y;
367 
368     /* First interpolate odd lines into the target buffer */
369     if (!deint_interpolate(src, dest, width, height, Bpp))
370         return 0;
371 
372     /* Now interpolate even lines in the source buffer; we don't use it
373      * after this so it's okay to destroy it */
374     ac_memcpy(src, src+Bpl, Bpl);
375     for (y = 2; y < height-1; y += 2)
376         ac_average(src + (y-1)*Bpl, src + (y+1)*Bpl, src + y*Bpl, Bpl);
377     if (y < height)
378         ac_memcpy(src + y*Bpl, src + (y-1)*Bpl, Bpl);
379 
380     /* Finally average the two frames together */
381     ac_average(src, dest, dest, height*Bpl);
382 
383     return 1;
384 }
385 
386 /*************************************************************************/
387 
388 /**
389  * tcv_resize:  Resize the given image using a lookup table.  `scale_w' and
390  * `scale_h' are the number of blocks the image is divided into (normally
391  * 8; 4 for subsampled U/V).  `resize_w' and `resize_h' are given in units
392  * of `scale_w' and `scale_h' respectively.  Only one of `resize_w' and
393  * `resize_h' may be nonzero.
394  * N.B. doesn't work well if shrinking by more than a factor of 2 (only
395  *      averages 2 adjacent lines/pixels)
396  *
397  * Parameters:   handle: tcvideo handle.
398  *                  src: Source data plane.
399  *                 dest: Destination data plane.
400  *                width: Width of frame.
401  *               height: Height of frame.
402  *                  Bpp: Bytes (not bits!) per pixel.
403  *             resize_w: Amount to add to width, in units of `scale_w'.
404  *             resize_h: Amount to add to width, in units of `scale_h'.
405  *              scale_w: Size in pixels of a `resize_w' unit.
406  *              scale_h: Size in pixels of a `resize_h' unit.
407  * Return value: Nonzero on success, zero on error (invalid parameters).
408  * Preconditions: handle != 0: handle was returned by tcv_init()
409  *                src != NULL: src[0]..src[width*height*Bpp-1] are readable
410  *                dest != NULL:
411  *                    destw = width + resize_w*scale_w;
412  *                    desth = height + resize_h*scale_h;
413  *                    dest[0]..dest[destw*desth*Bpp-1] are writable
414  *                src != dest: src and dest do not overlap
415  * Postconditions: (on success) dest[0]..dest[destw*desth*Bpp-1] are set
416  */
417 
418 static inline void rescale_pixel(const uint8_t *src1, const uint8_t *src2,
419                                  uint8_t *dest, int bytes,
420                                  uint32_t weight1, uint32_t weight2);
421 
tcv_resize(TCVHandle handle,uint8_t * src,uint8_t * dest,int width,int height,int Bpp,int resize_w,int resize_h,int scale_w,int scale_h)422 int tcv_resize(TCVHandle handle,
423                uint8_t *src, uint8_t *dest, int width, int height, int Bpp,
424                int resize_w, int resize_h, int scale_w, int scale_h)
425 {
426     int new_w, new_h;
427 
428 
429     if (!src || !dest || width <= 0 || height <= 0 || (Bpp != 1 && Bpp != 3)) {
430         tc_log_error("libtcvideo", "tcv_resize: invalid frame parameters!");
431         return 0;
432     }
433     if ((scale_w != 1 && scale_w != 2 && scale_w != 4 && scale_w != 8)
434      || (scale_h != 1 && scale_h != 2 && scale_h != 4 && scale_h != 8)) {
435         tc_log_error("libtcvideo", "tcv_resize: invalid scale parameters!");
436         return 0;
437     }
438     if (width % scale_w != 0 || height % scale_h != 0) {
439         tc_log_error("libtcvideo", "tcv_resize: scale parameters (%d,%d)"
440                      "invalid for frame size %dx%d",
441                      scale_w, scale_h, width, height);
442         return 0;
443     }
444 
445     new_w = width + resize_w*scale_w;
446     new_h = height + resize_h*scale_h;
447     if (new_w <= 0 || new_h <= 0) {
448         tc_log_error("libtcvideo", "tcv_resize: resizing parameters"
449                      " (%d,%d,%d,%d) invalid for frame size %dx%d",
450                      resize_w, resize_h, scale_w, scale_h, width, height);
451         return 0;
452     }
453 
454     /* Resize vertically (fast, using accelerated routine) */
455     if (resize_h) {
456         int Bpl = width * Bpp;  /* bytes per line */
457         int i, y;
458 
459         init_resize_tables(handle, 0, 0, height*8/scale_h, new_h*8/scale_h);
460         for (i = 0; i < scale_h; i++) {
461             uint8_t *sptr = src  + (i * (height/scale_h)) * Bpl;
462             uint8_t *dptr = dest + (i * (new_h /scale_h)) * Bpl;
463             for (y = 0; y < new_h / scale_h; y++) {
464                 ac_rescale(sptr + (handle->resize_table_y[y].source  ) * Bpl,
465                            sptr + (handle->resize_table_y[y].source+1) * Bpl,
466                            dptr + y*Bpl, Bpl,
467                            handle->resize_table_y[y].weight1,
468                            handle->resize_table_y[y].weight2);
469             }
470         }
471     }
472 
473     /* Resize horizontally; calling the accelerated routine for each pixel
474      * has far too much overhead, so we just perform the calculations
475      * directly. */
476     if (resize_w) {
477         int i, x;
478 
479         init_resize_tables(handle, width*8/scale_w, new_w*8/scale_w, 0, 0);
480         /* Treat the image as an array of blocks */
481         for (i = 0; i < new_h * scale_w; i++) {
482             /* This `if' is an optimization hint to the compiler, to
483              * suggest that it generate a separate version of the loop
484              * code for Bpp==1 without the unnecessary multiply ops. */
485             if (Bpp == 1) {  /* optimization hint */
486                 uint8_t *sptr = src  + (i * (width/scale_w)) * Bpp;
487                 uint8_t *dptr = dest + (i * (new_w/scale_w)) * Bpp;
488                 for (x = 0; x < new_w / scale_w; x++) {
489                     rescale_pixel(sptr + (handle->resize_table_x[x].source  ) * Bpp,
490                                   sptr + (handle->resize_table_x[x].source+1) * Bpp,
491                                   dptr + x*Bpp, Bpp,
492                                   handle->resize_table_x[x].weight1,
493                                   handle->resize_table_x[x].weight2);
494                 }
495             } else {  /* exactly the same thing */
496                 uint8_t *sptr = src  + (i * (width/scale_w)) * Bpp;
497                 uint8_t *dptr = dest + (i * (new_w/scale_w)) * Bpp;
498                 for (x = 0; x < new_w / scale_w; x++) {
499                     rescale_pixel(sptr + (handle->resize_table_x[x].source  ) * Bpp,
500                                   sptr + (handle->resize_table_x[x].source+1) * Bpp,
501                                   dptr + x*Bpp, Bpp,
502                                   handle->resize_table_x[x].weight1,
503                                   handle->resize_table_x[x].weight2);
504                 }
505             }
506         }
507     }
508 
509     return 1;
510 }
511 
rescale_pixel(const uint8_t * src1,const uint8_t * src2,uint8_t * dest,int bytes,uint32_t weight1,uint32_t weight2)512 static inline void rescale_pixel(const uint8_t *src1, const uint8_t *src2,
513                                  uint8_t *dest, int bytes,
514                                  uint32_t weight1, uint32_t weight2)
515 {
516     int byte;
517     for (byte = 0; byte < bytes; byte++) {
518         /* Watch out for trying to access beyond the end of the frame on
519          * the last pixel */
520         if (weight1 < 0x10000)  /* this is the more likely case */
521             dest[byte] = (src1[byte]*weight1 + src2[byte]*weight2 + 32768)
522                          >> 16;
523         else
524             dest[byte] = src1[byte];
525     }
526 }
527 
528 /*************************************************************************/
529 
530 /**
531  * tcv_zoom:  Resize the given image to an arbitrary size, with filtering.
532  *
533  * Parameters: handle: tcvideo handle.
534  *                src: Source data plane.
535  *               dest: Destination data plane.
536  *              width: Width of frame.
537  *             height: Height of frame.
538  *                Bpp: Bytes (not bits!) per pixel.
539  *              new_w: New frame width.
540  *              new_h: New frame height.  If negative, the frame is
541  *                     processed in an interlaced mode, zooming each
542  *                     field separately to a total height of -new_h.
543  *                     Both `height' and `new_h' must be even.
544  *             filter: Filter type (TCV_ZOOM_*).
545  * Return value: Nonzero on success, zero on error (invalid parameters).
546  * Preconditions: handle != 0: handle was returned by tcv_init()
547  *                src != NULL: src[0]..src[width*height*Bpp-1] are readable
548  *                dest != NULL: dest[0]..dest[new_w*new_h*Bpp-1] are writable
549  *                src != dest: src and dest do not overlap
550  * Postconditions: (on success) dest[0]..dest[new_w*new_h*Bpp-1] are set
551  */
552 
tcv_zoom(TCVHandle handle,uint8_t * src,uint8_t * dest,int width,int height,int Bpp,int new_w,int new_h,TCVZoomFilter filter)553 int tcv_zoom(TCVHandle handle,
554              uint8_t *src, uint8_t *dest, int width, int height, int Bpp,
555              int new_w, int new_h, TCVZoomFilter filter)
556 {
557     ZoomInfo *zi;
558     int free_zi = 0;  // Should the ZoomInfo be freed after use?
559     int interlace_mode = 0;
560     int i;
561 
562     if (!src || !dest || width <= 0 || height <= 0 || (Bpp != 1 && Bpp != 3)) {
563         tc_log_error("libtcvideo", "tcv_zoom: invalid frame parameters!");
564         return 0;
565     }
566     if (new_h < 0) {
567         new_h = -new_h;
568         interlace_mode = 1;
569         if (height % 2 != 0 || new_h % 2 != 0) {
570             tc_log_error("libtcvideo", "tcv_zoom: heights must be even in"
571                          " interlace mode (old height %d, new height %d)",
572                          height, new_h);
573             return 0;
574         }
575     }
576     if (new_w <= 0 || new_h <= 0) {
577         tc_log_error("libtcvideo", "tcv_zoom: invalid target size %dx%d!",
578                      new_w, new_h);
579         return 0;
580     }
581     switch (filter) {
582       case TCV_ZOOM_BOX:
583       case TCV_ZOOM_TRIANGLE:
584       case TCV_ZOOM_HERMITE:
585       case TCV_ZOOM_BELL:
586       case TCV_ZOOM_B_SPLINE:
587       case TCV_ZOOM_MITCHELL:
588       case TCV_ZOOM_LANCZOS3:
589         break;
590       default:
591         tc_log_error("libtcvideo", "tcv_zoom: invalid filter %d!", filter);
592         return 0;
593     }
594 
595     for (i = 0, zi = NULL; i < ZOOMINFO_CACHE_SIZE && zi == NULL; i++) {
596         if (handle->zoominfo_cache[i].zi     != NULL
597          && handle->zoominfo_cache[i].old_w  == width
598          && handle->zoominfo_cache[i].old_h  == height
599          && handle->zoominfo_cache[i].new_w  == new_w
600          && handle->zoominfo_cache[i].new_h  == new_h
601          && handle->zoominfo_cache[i].Bpp    == Bpp
602          && handle->zoominfo_cache[i].ilace  == interlace_mode
603          && handle->zoominfo_cache[i].filter == filter
604         ) {
605             zi = handle->zoominfo_cache[i].zi;
606         }
607     }
608     if (!zi) {
609         int ilace_height = height;
610         int ilace_new_h = new_h;
611         int old_stride = width * Bpp;
612         int new_stride = new_w * Bpp;
613         if (interlace_mode) {
614             ilace_height /= 2;
615             ilace_new_h /= 2;
616             old_stride *= 2;
617             new_stride *= 2;
618         }
619         zi = zoom_init(width, ilace_height, new_w, ilace_new_h, Bpp,
620                        old_stride, new_stride, filter);
621         if (!zi) {
622             tc_log_error("libtcvideo", "tcv_zoom: zoom_init() failed!");
623             return 0;
624         }
625         free_zi = 1;
626         for (i = 0; i < ZOOMINFO_CACHE_SIZE; i++) {
627             if (!handle->zoominfo_cache[i].zi) {
628                 handle->zoominfo_cache[i].zi     = zi;
629                 handle->zoominfo_cache[i].old_w  = width;
630                 handle->zoominfo_cache[i].old_h  = height;
631                 handle->zoominfo_cache[i].new_w  = new_w;
632                 handle->zoominfo_cache[i].new_h  = new_h;
633                 handle->zoominfo_cache[i].Bpp    = Bpp;
634                 handle->zoominfo_cache[i].ilace  = interlace_mode;
635                 handle->zoominfo_cache[i].filter = filter;
636                 free_zi = 0;
637                 break;
638             }
639         }
640     }
641     zoom_process(zi, src, dest);
642     if (interlace_mode)
643         zoom_process(zi, src + width*Bpp, dest + new_w*Bpp);
644     if (free_zi)
645         zoom_free(zi);
646     return 1;
647 }
648 
649 /*************************************************************************/
650 
651 /**
652  * tcv_reduce:  Efficiently reduce the image size by a specified integral
653  * amount, by removing intervening pixels.
654  *
655  * Parameters:   handle: tcvideo handle.
656  *                  src: Source data plane.
657  *                 dest: Destination data plane.
658  *                width: Width of frame.
659  *               height: Height of frame.
660  *                  Bpp: Bytes (not bits!) per pixel.
661  *                new_w: New frame width.
662  *                new_h: New frame height.
663  *             reduce_w: Ratio to reduce width by.
664  *             reduce_h: Ratio to reduce height by.
665  * Return value: Nonzero on success, zero on error (invalid parameters).
666  * Preconditions: handle != 0: handle was returned by tcv_init()
667  *                src != NULL: src[0]..src[width*height*Bpp-1] are readable
668  *                dest != NULL && reduce_w > 0 && reduce_h > 0:
669  *                    destw = width / reduce_w;
670  *                    desth = height / reduce_h;
671  *                    dest[0]..dest[destw*desth*Bpp-1] are writable
672  *                src != dest: src and dest do not overlap
673  * Postconditions: (on success) dest[0]..dest[destw*desth*Bpp-1] are set
674  */
675 
tcv_reduce(TCVHandle handle,uint8_t * src,uint8_t * dest,int width,int height,int Bpp,int reduce_w,int reduce_h)676 int tcv_reduce(TCVHandle handle,
677                uint8_t *src, uint8_t *dest, int width, int height, int Bpp,
678                int reduce_w, int reduce_h)
679 {
680     if (!src || !dest || width <= 0 || height <= 0 || (Bpp != 1 && Bpp != 3)) {
681         tc_log_error("libtcvideo", "tcv_reduce: invalid frame parameters!");
682         return 0;
683     }
684     if (reduce_w <= 0 || reduce_h <= 0) {
685         tc_log_error("libtcvideo", "tcv_reduce: invalid reduction parameters"
686                      " (%d,%d)!", reduce_w, reduce_h);
687         return 0;
688     }
689 
690     if (reduce_w != 1) {
691         /* Standard case: width and (possibly) height are being reduced */
692         int x, y, i;
693         int xstep = Bpp * reduce_w;
694         for (y = 0; y < height / reduce_h; y++) {
695             for (x = 0; x < width / reduce_w; x++) {
696                 for (i = 0; i < Bpp; i++)
697                     *dest++ = src[x*xstep+i];
698             }
699             src += width*Bpp * reduce_h;
700         }
701     } else if (reduce_h != 1) {
702         /* Optimized case 1: only height is being reduced */
703         int y;
704         int Bpl = width * Bpp;
705         for (y = 0; y < height / reduce_h; y++)
706             ac_memcpy(dest + y*Bpl, src + y*(Bpl*reduce_h), Bpl);
707     } else {
708         /* Optimized case 2: no reduction, direct copy */
709         ac_memcpy(dest, src, width*height*Bpp);
710     }
711 
712     return 1;
713 }
714 
715 /*************************************************************************/
716 
717 /**
718  * tcv_flip_v:  Flip the given image vertically.
719  *
720  * Parameters: handle: tcvideo handle.
721  *                src: Source data plane.
722  *               dest: Destination data plane.
723  *              width: Width of frame.
724  *             height: Height of frame.
725  *                Bpp: Bytes (not bits!) per pixel.
726  * Return value: Nonzero on success, zero on error (invalid parameters).
727  * Preconditions: handle != 0: handle was returned by tcv_init()
728  *                src != NULL: src[0]..src[width*height*Bpp-1] are readable
729  *                dest != NULL: dest[0]..dest[width*height*Bpp-1] are writable
730  *                src != dest: src and dest do not overlap
731  * Postconditions: (on success) dest[0]..dest[width*height*Bpp-1] are set
732  */
733 
tcv_flip_v(TCVHandle handle,uint8_t * src,uint8_t * dest,int width,int height,int Bpp)734 int tcv_flip_v(TCVHandle handle,
735                uint8_t *src, uint8_t *dest, int width, int height, int Bpp)
736 {
737     int Bpl = width * Bpp;  /* bytes per line */
738     int y;
739     uint8_t buf[TC_MAX_V_FRAME_WIDTH * TC_MAX_V_BYTESPP];
740 
741     if (!src || !dest || width <= 0 || height <= 0 || (Bpp != 1 && Bpp != 3)) {
742         tc_log_error("libtcvideo", "tcv_flip_v: invalid frame parameters!");
743         return 0;
744     }
745 
746     /* Note that GCC4 can optimize this perfectly; no need for extra
747      * pointer variables */
748     if (src != dest) {
749         for (y = 0; y < height; y++) {
750             ac_memcpy(dest + ((height-1)-y)*Bpl, src + y*Bpl, Bpl);
751         }
752     } else {
753         for (y = 0; y < (height+1)/2; y++) {
754             ac_memcpy(buf, src + y*Bpl, Bpl);
755             ac_memcpy(dest + y*Bpl, src + ((height-1)-y)*Bpl, Bpl);
756             ac_memcpy(dest + ((height-1)-y)*Bpl, buf, Bpl);
757         }
758     }
759 
760     return 1;
761 }
762 
763 /*************************************************************************/
764 
765 /**
766  * tcv_flip_h:  Flip the given image horizontally.
767  *
768  * Parameters: handle: tcvideo handle.
769  *                src: Source data plane.
770  *               dest: Destination data plane.
771  *              width: Width of frame.
772  *             height: Height of frame.
773  *                Bpp: Bytes (not bits!) per pixel.
774  * Return value: Nonzero on success, zero on error (invalid parameters).
775  * Preconditions: handle != 0: handle was returned by tcv_init()
776  *                src != NULL: src[0]..src[width*height*Bpp-1] are readable
777  *                dest != NULL: dest[0]..dest[width*height*Bpp-1] are writable
778  *                src != dest: src and dest do not overlap
779  * Postconditions: (on success) dest[0]..dest[width*height*Bpp-1] are set
780  */
781 
tcv_flip_h(TCVHandle handle,uint8_t * src,uint8_t * dest,int width,int height,int Bpp)782 int tcv_flip_h(TCVHandle handle,
783                uint8_t *src, uint8_t *dest, int width, int height, int Bpp)
784 {
785     int x, y, i;
786 
787     if (!src || !dest || width <= 0 || height <= 0 || (Bpp != 1 && Bpp != 3)) {
788         tc_log_error("libtcvideo", "tcv_flip_h: invalid frame parameters!");
789         return 0;
790     }
791 
792     for (y = 0; y < height; y++) {
793         uint8_t *srcline = src + y*width*Bpp;
794         uint8_t *destline = dest + y*width*Bpp;
795         if (src != dest) {
796             for (x = 0; x < width; x++) {
797                 for (i = 0; i < Bpp; i++) {
798                     destline[((width-1)-x)*Bpp+i] = srcline[x*Bpp+i];
799                 }
800             }
801         } else {
802             for (x = 0; x < (width+1)/2; x++) {
803                 for (i = 0; i < Bpp; i++) {
804                     uint8_t tmp = srcline[x*Bpp+i];
805                     destline[x*Bpp+i] = srcline[((width-1)-x)*Bpp+i];
806                     destline[((width-1)-x)*Bpp+i] = tmp;
807                 }
808             }
809         }
810     }
811 
812     return 1;
813 }
814 
815 /*************************************************************************/
816 
817 /**
818  * tcv_gamma_correct:  Perform gamma correction on the given image.
819  *
820  * Parameters: handle: tcvideo handle.
821  *                src: Source data plane.
822  *               dest: Destination data plane.
823  *              width: Width of frame.
824  *             height: Height of frame.
825  *                Bpp: Bytes (not bits!) per pixel.
826  *              gamma: Gamma value.
827  * Return value: Nonzero on success, zero on error (invalid parameters).
828  * Preconditions: handle != 0: handle was returned by tcv_init()
829  *                src != NULL: src[0]..src[width*height*Bpp-1] are readable
830  *                dest != NULL: dest[0]..dest[width*height*Bpp-1] are writable
831  *                src != dest: src and dest do not overlap
832  * Postconditions: (on success) dest[0]..dest[width*height*Bpp-1] are set
833  */
834 
tcv_gamma_correct(TCVHandle handle,uint8_t * src,uint8_t * dest,int width,int height,int Bpp,double gamma)835 int tcv_gamma_correct(TCVHandle handle,
836                       uint8_t *src, uint8_t *dest, int width, int height,
837                       int Bpp, double gamma)
838 {
839     int i;
840 
841     if (!src || !dest || width <= 0 || height <= 0 || (Bpp != 1 && Bpp != 3)) {
842         tc_log_error("libtcvideo", "tcv_gamma: invalid frame parameters!");
843         return 0;
844     }
845     if (gamma <= 0) {
846         tc_log_error("libtcvideo", "tcv_gamma: invalid gamma (%.3f)!", gamma);
847         return 0;
848     }
849 
850     init_gamma_table(handle, gamma);
851     for (i = 0; i < width*height*Bpp; i++)
852         dest[i] = handle->gamma_table[src[i]];
853 
854     return 1;
855 }
856 
857 /*************************************************************************/
858 
859 /**
860  * tcv_antialias:  Perform antialiasing on the given image.
861  *
862  * Parameters: handle: tcvideo handle.
863  *                src: Source data plane.
864  *               dest: Destination data plane.
865  *              width: Width of frame.
866  *             height: Height of frame.
867  *                Bpp: Bytes (not bits!) per pixel.
868  *             weight: `weight' antialiasing parameter.
869  *               bias: `bias' antialiasing parameter.
870  * Return value: Nonzero on success, zero on error (invalid parameters).
871  * Preconditions: handle != 0: handle was returned by tcv_init()
872  *                src != NULL: src[0]..src[width*height*Bpp-1] are readable
873  *                dest != NULL: dest[0]..dest[width*height*Bpp-1] are writable
874  *                src != dest: src and dest do not overlap
875  * Postconditions: (on success) dest[0]..dest[width*height*Bpp-1] are set
876  */
877 
878 static void antialias_line(TCVHandle handle,
879                            uint8_t *src, uint8_t *dest, int width, int Bpp);
880 
tcv_antialias(TCVHandle handle,uint8_t * src,uint8_t * dest,int width,int height,int Bpp,double weight,double bias)881 int tcv_antialias(TCVHandle handle,
882                   uint8_t *src, uint8_t *dest, int width, int height,
883                   int Bpp, double weight, double bias)
884 {
885     int y;
886 
887     if (!src || !dest || width <= 0 || height <= 0 || (Bpp != 1 && Bpp != 3)) {
888         tc_log_error("libtcvideo", "tcv_antialias: invalid frame parameters!");
889         return 0;
890     }
891     if (weight < 0 || weight > 1 || bias < 0 || bias > 1) {
892         tc_log_error("libtcvideo", "tcv_antialias: invalid antialiasing"
893                      " parameters (weight=%.3f, bias=%.3f)", weight, bias);
894         return 0;
895     }
896 
897     init_aa_table(handle, weight, bias);
898     ac_memcpy(dest, src, width*Bpp);
899     for (y = 1; y < height-1; y++) {
900         antialias_line(handle, src + y*width*Bpp, dest + y*width*Bpp, width,
901                        Bpp);
902     }
903     ac_memcpy(dest + (height-1)*width*Bpp, src + (height-1)*width*Bpp,
904               width*Bpp);
905 
906     return 1;
907 }
908 
909 
910 /* Helper functions: */
911 
samecolor(uint8_t * pixel1,uint8_t * pixel2,int Bpp)912 static inline int samecolor(uint8_t *pixel1, uint8_t *pixel2, int Bpp)
913 {
914     int i;
915     int maxdiff = abs(pixel2[0]-pixel1[0]);
916     for (i = 1; i < Bpp; i++) {
917         int diff = abs(pixel2[i]-pixel1[i]);
918         if (diff > maxdiff)
919             maxdiff = diff;
920     }
921     return maxdiff < AA_DIFFERENT;
922 }
923 
924 #define C (src + x*Bpp)
925 #define U (C - width*Bpp)
926 #define D (C + width*Bpp)
927 #define L (C - Bpp)
928 #define R (C + Bpp)
929 #define UL (U - Bpp)
930 #define UR (U + Bpp)
931 #define DL (D - Bpp)
932 #define DR (D + Bpp)
933 #define SAME(pix1,pix2) samecolor((pix1),(pix2),Bpp)
934 #define DIFF(pix1,pix2) !samecolor((pix1),(pix2),Bpp)
935 
antialias_line(TCVHandle handle,uint8_t * src,uint8_t * dest,int width,int Bpp)936 static void antialias_line(TCVHandle handle,
937                            uint8_t *src, uint8_t *dest, int width, int Bpp)
938 {
939     int i, x;
940 
941     for (i = 0; i < Bpp; i++)
942         dest[i] = src[i];
943     for (x = 1; x < width-1; x++) {
944         if ((SAME(L,U) && DIFF(L,D) && DIFF(L,R))
945          || (SAME(L,D) && DIFF(L,U) && DIFF(L,R))
946          || (SAME(R,U) && DIFF(R,D) && DIFF(R,L))
947          || (SAME(R,D) && DIFF(R,U) && DIFF(R,L))
948         ) {
949             for (i = 0; i < Bpp; i++) {
950                 uint32_t tmp = handle->aa_table_d[UL[i]]
951                              + handle->aa_table_y[U [i]]
952                              + handle->aa_table_d[UR[i]]
953                              + handle->aa_table_x[L [i]]
954                              + handle->aa_table_c[C [i]]
955                              + handle->aa_table_x[R [i]]
956                              + handle->aa_table_d[DL[i]]
957                              + handle->aa_table_y[D [i]]
958                              + handle->aa_table_d[DR[i]]
959                              + 32768;
960                 dest[x*Bpp+i] = (verbose & TC_DEBUG) ? 255 : tmp>>16;
961             }
962         } else {
963             for (i = 0; i < Bpp; i++)
964                 dest[x*Bpp+i] = src[x*Bpp+i];
965         }
966     }
967     for (i = 0; i < Bpp; i++)
968         dest[(width-1)*Bpp+i] = src[(width-1)*Bpp+i];
969 }
970 
971 /*************************************************************************/
972 
973 /**
974  * tcv_convert:  Convert an image from one image format to another.  The
975  * source and destination image pointers can be the same, causing the image
976  * to be converted in place (in this case a temporary buffer will be
977  * allocated as necessary to perform the conversion).
978  *
979  * Parameters:  handle: tcvideo handle.
980  *                 src: Image to convert.
981  *                dest: Buffer for converted image.
982  *               width: Width of image.
983  *              height: Height of image.
984  *              srcfmt: Format of image.
985  *             destfmt: Format to convert image to.
986  * Return value: Nonzero on success, zero on error (invalid parameters).
987  * Preconditions: handle != 0: handle was returned by tcv_init()
988  * Postconditions: None.
989  */
990 
tcv_convert(TCVHandle handle,uint8_t * src,uint8_t * dest,int width,int height,ImageFormat srcfmt,ImageFormat destfmt)991 int tcv_convert(TCVHandle handle, uint8_t *src, uint8_t *dest, int width,
992                 int height, ImageFormat srcfmt, ImageFormat destfmt)
993 {
994     uint8_t *realdest;  // either dest or the temporary buffer
995     uint8_t *srcplanes[3], *destplanes[3];
996     uint32_t size;
997 
998     if (!handle) {
999         tc_log_error("libtcvideo", "tcv_convert(): No handle given!");
1000         return 0;
1001     }
1002     if (!src || !dest || width <= 0 || height <= 0 || !srcfmt || !destfmt) {
1003         tc_log_error("libtcvideo", "tcv_convert(): Invalid image parameters!");
1004         return 0;
1005     }
1006 
1007     switch (destfmt) {
1008         case IMG_YUV420P:
1009         case IMG_YV12   : size = width*height + (width/2)*(height/2)*2; break;
1010         case IMG_YUV411P: size = width*height + (width/4)*height*2; break;
1011         case IMG_YUV422P: size = width*height + (width/2)*height*2; break;
1012         case IMG_YUV444P: size = width*height*3; break;
1013         case IMG_YUY2   :
1014         case IMG_UYVY   :
1015         case IMG_YVYU   : size = (width*2)*height; break;
1016         case IMG_Y8     :
1017         case IMG_GRAY8  : size = width*height; break;
1018         case IMG_RGB24  :
1019         case IMG_BGR24  : size = (width*3)*height; break;
1020         case IMG_RGBA32 :
1021         case IMG_ABGR32 :
1022         case IMG_ARGB32 :
1023         case IMG_BGRA32 : size = (width*4)*height; break;
1024         default         : return 0;
1025     }
1026 
1027     if (srcfmt == destfmt) {
1028         /* Formats are the same, just copy (if needed) and return */
1029         if (src != dest)
1030             ac_memcpy(dest, src, size);
1031         return 1;
1032     }
1033 
1034     if (src == dest) {
1035         /* In-place conversion, so allocate a properly-sized buffer */
1036         if (!handle->convert_buffer || handle->convert_buffer_size < size) {
1037             free(handle->convert_buffer);
1038             handle->convert_buffer = tc_malloc(size);
1039             if (!handle->convert_buffer)
1040                 return 0;
1041             handle->convert_buffer_size = size;
1042         }
1043         realdest = handle->convert_buffer;
1044     } else {
1045         realdest = dest;
1046     }
1047 
1048     YUV_INIT_PLANES(srcplanes, src, srcfmt, width, height);
1049     YUV_INIT_PLANES(destplanes, realdest, destfmt, width, height);
1050     if (!ac_imgconvert(srcplanes, srcfmt, destplanes, destfmt, width, height))
1051         return 0;
1052 
1053     if (src == dest)
1054         ac_memcpy(src, handle->convert_buffer, size);
1055 
1056     return 1;
1057 }
1058 
1059 /*************************************************************************/
1060 /*************************************************************************/
1061 
1062 /* Internal-use helper functions. */
1063 
1064 /*************************************************************************/
1065 
1066 /**
1067  * init_resize_tables:  Initialize the lookup tables used for resizing.  If
1068  * either of `oldw' and `neww' is nonpositive, the horizontal resizing
1069  * table will not be initialized; likewise for `oldh', `newh', and the
1070  * vertical resizing table.  Initialization will also not be performed if
1071  * the values given are the same as in the previous call (thus repeated
1072  * calls with the same values suffer only the penalty of entering and
1073  * exiting the procedure).  Note the order of parameters!
1074  *
1075  * Parameters: handle: tcvideo handle.
1076  *               oldw: Original image width.
1077  *               neww: New image width.
1078  *               oldh: Original image height.
1079  *               newh: New image height.
1080  * Return value: None.
1081  * Preconditions: handle != 0
1082  *                oldw % 8 == 0
1083  *                neww % 8 == 0
1084  *                oldh % 8 == 0
1085  *                newh % 8 == 0
1086  * Postconditions: If oldw > 0 && neww > 0:
1087  *                     resize_table_x[0..neww/8-1] are initialized
1088  *                 If oldh > 0 && newh > 0:
1089  *                     resize_table_y[0..newh/8-1] are initialized
1090  */
1091 
init_resize_tables(TCVHandle handle,int oldw,int neww,int oldh,int newh)1092 static void init_resize_tables(TCVHandle handle,
1093                                int oldw, int neww, int oldh, int newh)
1094 {
1095     if (oldw > 0 && neww > 0
1096      && (oldw != handle->saved_oldw || neww != handle->saved_neww)
1097     ) {
1098         init_one_resize_table(handle->resize_table_x, oldw, neww);
1099         handle->saved_oldw = oldw;
1100         handle->saved_neww = neww;
1101     }
1102     if (oldh > 0 && newh > 0
1103      && (oldh != handle->saved_oldh || newh != handle->saved_newh)
1104     ) {
1105         init_one_resize_table(handle->resize_table_y, oldh, newh);
1106         handle->saved_oldh = oldh;
1107         handle->saved_newh = newh;
1108     }
1109 }
1110 
1111 
1112 /**
1113  * init_one_resize_table:  Helper function for init_resize_tables() to
1114  * initialize a single table.
1115  *
1116  * Parameters:   table: Table to initialize.
1117  *             oldsize: Size to resize from.
1118  *             newsize: Size to resize to.
1119  * Return value: None.
1120  * Preconditions: table != NULL
1121  *                oldsize > 0
1122  *                oldsize % 8 == 0
1123  *                newsize > 0
1124  *                newsize % 8 == 0
1125  * Postconditions: table[0..newsize/8-1] are initialized
1126  */
1127 
init_one_resize_table(struct resize_table_elem * table,int oldsize,int newsize)1128 static void init_one_resize_table(struct resize_table_elem *table,
1129                                   int oldsize, int newsize)
1130 {
1131     int i;
1132 
1133     /* Compute the number of source pixels per destination pixel */
1134     double width_ratio = (double)oldsize / (double)newsize;
1135 
1136     for (i = 0; i < newsize/8; i++) {
1137         double oldpos;
1138 
1139         /* Left/topmost source pixel to use */
1140         oldpos = (double)i * (double)oldsize / (double)newsize;
1141         table[i].source = (int)oldpos;
1142 
1143         /* Is the new pixel contained entirely within the old? */
1144         if (oldpos+width_ratio < table[i].source+1) {
1145             /* Yes, weight ratio is 1.0:0.0 */
1146             table[i].weight1 = 65536;
1147             table[i].weight2 = 0;
1148         } else {
1149             /* No, compute appropriate weight ratio */
1150             double temp = ((table[i].source+1) - oldpos) / width_ratio * PI/2;
1151             table[i].weight1 = (uint32_t)(sin(temp)*sin(temp) * 65536 + 0.5);
1152             table[i].weight2 = 65536 - table[i].weight1;
1153         }
1154     }
1155 }
1156 
1157 /*************************************************************************/
1158 
1159 /**
1160  * init_gamma_table:  Initialize the gamma correction lookup table.
1161  * Initialization will not be performed for repeated calls with the same
1162  * value.
1163  *
1164  * Parameters: handle: tcvideo handle.
1165  *              gamma: Gamma value.
1166  * Return value: None.
1167  * Preconditions: handle != 0
1168  *                gamma > 0
1169  * Postconditions: gamma_table[0..255] are initialized
1170  */
1171 
init_gamma_table(TCVHandle handle,double gamma)1172 static void init_gamma_table(TCVHandle handle, double gamma)
1173 {
1174     if (gamma != handle->saved_gamma) {
1175         int i;
1176         for (i = 0; i < 256; i++)
1177             handle->gamma_table[i] = (uint8_t) (pow((i/255.0),gamma) * 255);
1178         handle->saved_gamma = gamma;
1179     }
1180 }
1181 
1182 /*************************************************************************/
1183 
1184 /**
1185  * init_handle->aa_table:  Initialize the antialiasing lookup tables.
1186  * Initialization will not be performed for repeated calls with the same
1187  * values.
1188  *
1189  * Parameters:    handle: tcvideo handle.
1190  *             aa_weight: Antialiasing weight value.
1191  *               aa_bias: Antialiasing bias value.
1192  * Return value: None.
1193  * Preconditions: handle != 0
1194  *                0 <= aa_weight && aa_weight <= 1
1195  *                0 <= aa_bias && aa_bias <= 1
1196  * Postconditions: gamma_table[0..255] are initialized
1197  */
1198 
init_aa_table(TCVHandle handle,double aa_weight,double aa_bias)1199 static void init_aa_table(TCVHandle handle, double aa_weight, double aa_bias)
1200 {
1201     if (aa_weight != handle->saved_weight || aa_bias != handle->saved_bias) {
1202         int i;
1203         for (i = 0; i < 256; ++i) {
1204             handle->aa_table_c[i] = i*aa_weight * 65536;
1205             handle->aa_table_x[i] = i*aa_bias*(1-aa_weight)/4 * 65536;
1206             handle->aa_table_y[i] = i*(1-aa_bias)*(1-aa_weight)/4 * 65536;
1207             handle->aa_table_d[i] =
1208                 (handle->aa_table_x[i]+handle->aa_table_y[i]+1)/2;
1209         }
1210         handle->saved_weight = aa_weight;
1211         handle->saved_bias = aa_bias;
1212     }
1213 }
1214 
1215 /*************************************************************************/
1216 /*************************************************************************/
1217 
1218 /*
1219  * Local variables:
1220  *   c-file-style: "stroustrup"
1221  *   c-file-offsets: ((case-label . *) (statement-case-intro . *))
1222  *   indent-tabs-mode: nil
1223  * End:
1224  *
1225  * vim: expandtab shiftwidth=4:
1226  */
1227