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