1 /*
2 * filter_logo.c
3 *
4 * Copyright (C) Tilmann Bitterberg - April 2002
5 * filter updates, enhancements and cleanup:
6 * Copyright (C) Sebastian Kun <seb at sarolta dot com> - March 2006
7 *
8 * This file is part of transcode, a video stream processing tool
9 *
10 * transcode is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2, or (at your option)
13 * any later version.
14 *
15 * transcode is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with GNU Make; see the file COPYING. If not, write to
22 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 *
24 */
25
26 /* TODO:
27 - animated gif/png support -> done
28 - sequences of jpgs maybe
29 would be nice.
30 */
31
32 #define MOD_NAME "filter_logo.so"
33 #define MOD_VERSION "v0.10 (2003-10-16)"
34 #define MOD_CAP "render image in videostream"
35 #define MOD_AUTHOR "Tilmann Bitterberg"
36
37 /* Note: because of ImageMagick bogosity, this must be included first, so
38 * we can undefine the PACKAGE_* symbols it splats into our namespace */
39 #include <magick/api.h>
40 #undef PACKAGE_BUGREPORT
41 #undef PACKAGE_NAME
42 #undef PACKAGE_STRING
43 #undef PACKAGE_TARNAME
44 #undef PACKAGE_VERSION
45
46 /* Add workaround for deprecated ScaleCharToQuantum() function */
47 #undef ScaleCharToQuantum
48 #if MAGICKCORE_QUANTUM_DEPTH == 8
49 # define ScaleCharToQuantum(x) ((x))
50 #elif MAGICKCORE_QUANTUM_DEPTH == 16
51 # define ScaleCharToQuantum(x) ((x)*257)
52 #elif MAGICKCORE_QUANTUM_DEPTH == 32
53 # define ScaleCharToQuantum(x) ((x)*16843009)
54 #elif MAGICKCORE_QUANTUM_DEPTH == 64
55 # define ScaleCharToQuantum(x) ((x)*71777214294589695ULL)
56 #endif
57
58 #include "transcode.h"
59 #include "filter.h"
60 #include "libtc/libtc.h"
61 #include "libtc/optstr.h"
62 #include "libtcvideo/tcvideo.h"
63
64 #include <stdlib.h>
65 #include <stdio.h>
66
67 #define MAX_UINT8_VAL ((uint8_t)(-1))
68
69 // basic parameter
70
71 enum POS { NONE, TOP_LEFT, TOP_RIGHT, BOT_LEFT, BOT_RIGHT, CENTER };
72
73 typedef struct MyFilterData {
74 /* public */
75 char file[PATH_MAX]; /* input filename */
76 int posx; /* X offset in video */
77 int posy; /* Y offset in video */
78 enum POS pos; /* predifined position */
79 int flip; /* bool if to flip image */
80 int ignoredelay; /* allow the user to ignore delays */
81 int rgbswap; /* bool if swap colors */
82 int grayout; /* only render lume values */
83 int hqconv; /* do high quality rgb->yuv conv. */
84 unsigned int start, end; /* ranges */
85 unsigned int fadein; /* No. of frames to fade in */
86 unsigned int fadeout; /* No. of frames to fade out */
87
88 /* private */
89 unsigned int nr_of_images; /* animated: number of images */
90 unsigned int cur_seq; /* animated: current image */
91 int cur_delay; /* animated: current delay */
92 uint8_t **yuv; /* buffer for RGB->YUV conversion */
93
94 TCVHandle tcvhandle; /* handle for RGB->YUV conversion */
95
96 /* These used to be static (per-module), but are now per-instance. */
97 vob_t *vob; /* video info from transcode */
98 Image *image; /* Magick image handle */
99 Image *images; /* tmp Magick handle (todo:remove) */
100 } MyFilterData;
101
102 /* FIXME: this uses the filter ID as an index--the ID can grow
103 * arbitrarily large, so this needs to be fixed */
104 static MyFilterData *mfd_all[100] = {NULL};
105
106 /* Only one instance of the module needs to initialize ImageMagick */
107 static int magick_usecount = 0;
108
109 /* Coefficients used for transparency calculations. Pre-generating these
110 * in a lookup table provides a small speed boost.
111 */
112 static float img_coeff_lookup[MAX_UINT8_VAL + 1] = {-1.0};
113 static float vid_coeff_lookup[MAX_UINT8_VAL + 1] = {-1.0};
114
115 /* from /src/transcode.c */
116 extern int rgbswap;
117 extern int flip;
118 /* should probably honor the other flags too */
119
120 /*-------------------------------------------------
121 *
122 * single function interface
123 *
124 *-------------------------------------------------*/
125
flogo_help_optstr(void)126 static void flogo_help_optstr(void)
127 {
128 tc_log_info(MOD_NAME, "(%s) help\n"
129 "* Overview\n"
130 " This filter renders an user specified image into the video.\n"
131 " Any image format ImageMagick can read is accepted.\n"
132 " Transparent images are also supported.\n"
133 " Image origin is at the very top left.\n"
134 "\n"
135 "* Options\n"
136 " 'file' Image filename (required) [logo.png]\n"
137 " 'pos' Position (0-width x 0-height) [0x0]\n"
138 " 'posdef' Position (0=None, 1=TopL, 2=TopR, 3=BotL, 4=BotR, 5=Center) [0]\n"
139 " 'range' Restrict rendering to framerange (0-oo) [0-end]\n"
140 " 'fade' Fade image in/out (# of frames) (0-oo) [0-0]\n"
141 " 'flip' Mirror image (0=off, 1=on) [0]\n"
142 " 'rgbswap' Swap colors [0]\n"
143 " 'grayout' YUV only: don't write Cb and Cr, makes a nice effect [0]\n"
144 " 'hqconv' YUV only: do high quality rgb->yuv img conversion [0]\n"
145 " 'ignoredelay' Ignore delay specified in animations [0]\n"
146 , MOD_CAP);
147 }
148
149
150 /**
151 * flogo_yuvbuf_free: Frees a set of YUV frame buffers allocated with
152 * flogo_yuvbuf_alloc().
153 * Parameters: yuv: a pointer to a set of YUV frames
154 * num: the number of frames to free
155 * Return value: N/A
156 * Preconditions: yuv was allocated with flogo_yuvbuf_alloc
157 * num > 0
158 * Postconditions: N/A
159 */
flogo_yuvbuf_free(uint8_t ** yuv,int num)160 static void flogo_yuvbuf_free(uint8_t **yuv, int num) {
161 int i;
162
163 if (yuv) {
164 for (i = 0; i < num; i++) {
165 if (yuv[i] != NULL)
166 tc_free(yuv[i]);
167 }
168 tc_free(yuv);
169 }
170
171 return;
172 }
173
174
175 /**
176 * flogo_yuvbuf_alloc: Allocates a set of zeroed YUV frame buffers.
177 *
178 * Parameters: size: the size of each frame
179 * num: the number of frames to allocate.
180 * Return value: An array of pointers to zeroed YUV buffers.
181 * Preconditions: size > 0
182 * num > 0
183 * Postconditions: The returned pointer should be freed with
184 * flogo_yuvbuf_free.
185 */
flogo_yuvbuf_alloc(size_t size,int num)186 static uint8_t **flogo_yuvbuf_alloc(size_t size, int num) {
187 uint8_t **yuv;
188 int i;
189
190 yuv = tc_zalloc(sizeof(uint8_t *) * num);
191 if (yuv == NULL)
192 return NULL;
193
194 for (i = 0; i < num; i++) {
195 yuv[i] = tc_malloc(sizeof(uint8_t) * size);
196 if (yuv[i] == NULL) {
197 // free what's already been allocated
198 flogo_yuvbuf_free(yuv, i-1);
199 return NULL;
200 }
201 }
202
203 return yuv;
204 }
205
206
207 /**
208 * flogo_convert_image: Converts a single ImageMagick RGB image into a format
209 * usable by transcode.
210 *
211 * Parameters: tcvhandle: Opaque libtcvideo handle
212 * src: An ImageMagick handle (the source image)
213 * dst: A pointer to the output buffer
214 * ifmt: The output format (see aclib/imgconvert.h)
215 * do_rgbswap: zero for no swap, nonzero to swap red and blue
216 * pixel positions
217 * Return value: 1 on success, 0 on failure
218 * Preconditions: tcvhandle != null, was returned by a call to tcv_init()
219 * src is a valid ImageMagick RGB image handle
220 * dst buffer is large enough to hold the result of the
221 * requested conversion
222 * Postconditions: dst get overwritten with the result of the conversion
223 */
flogo_convert_image(TCVHandle tcvhandle,Image * src,uint8_t * dst,ImageFormat ifmt,int do_rgbswap)224 static int flogo_convert_image(TCVHandle tcvhandle,
225 Image *src,
226 uint8_t *dst,
227 ImageFormat ifmt,
228 int do_rgbswap)
229 {
230 PixelPacket *pixel_packet;
231 uint8_t *dst_ptr = dst;
232
233 int row, col;
234 int height = src->rows;
235 int width = src->columns;
236 int ret;
237
238 unsigned long r_off, g_off, b_off;
239
240 if (!do_rgbswap) {
241 r_off = 0;
242 b_off = 2;
243 } else {
244 r_off = 2;
245 b_off = 0;
246 }
247 g_off = 1;
248
249 pixel_packet = GetImagePixels(src, 0, 0, width, height);
250
251 for (row = 0; row < height; row++) {
252 for (col = 0; col < width; col++) {
253 *(dst_ptr + r_off) = (uint8_t)ScaleQuantumToChar(pixel_packet->red);
254 *(dst_ptr + g_off) = (uint8_t)ScaleQuantumToChar(pixel_packet->green);
255 *(dst_ptr + b_off) = (uint8_t)ScaleQuantumToChar(pixel_packet->blue);
256
257 dst_ptr += 3;
258 pixel_packet++;
259 }
260 }
261
262 ret = tcv_convert(tcvhandle, dst, dst, width, height, IMG_RGB24, ifmt);
263 if (ret == 0) {
264 tc_log_error(MOD_NAME, "RGB->YUV conversion failed");
265 return 0;
266 }
267
268 return 1;
269 }
270
271
tc_filter(frame_list_t * ptr_,char * options)272 int tc_filter(frame_list_t *ptr_, char *options)
273 {
274 vframe_list_t *ptr = (vframe_list_t *)ptr_;
275 vob_t *vob = NULL;
276
277 int instance = ptr->filter_id;
278 MyFilterData *mfd = mfd_all[instance];
279
280 if (mfd != NULL) {
281 vob = mfd->vob;
282 }
283
284 //----------------------------------
285 //
286 // filter init
287 //
288 //----------------------------------
289
290
291 if (ptr->tag & TC_FILTER_GET_CONFIG) {
292 optstr_filter_desc(options, MOD_NAME, MOD_CAP, MOD_VERSION, MOD_AUTHOR, "VRYO", "1");
293 // buf, name, comment, format, val, from, to
294 optstr_param(options, "file", "Image filename", "%s", "logo.png");
295 optstr_param(options, "posdef", "Position (0=None, 1=TopL, 2=TopR, 3=BotL, 4=BotR, 5=Center)", "%d", "0", "0", "5");
296 optstr_param(options, "pos", "Position (0-width x 0-height)", "%dx%d", "0x0", "0", "width", "0", "height");
297 optstr_param(options, "range", "Restrict rendering to framerange", "%u-%u", "0-0", "0", "oo", "0", "oo");
298 optstr_param(options, "fade", "Fade image in/out (# of frames)", "%u-%u", "0-0", "0", "oo", "0", "oo");
299 // bools
300 optstr_param(options, "ignoredelay", "Ignore delay specified in animations", "", "0");
301 optstr_param(options, "rgbswap", "Swap red/blue colors", "", "0");
302 optstr_param(options, "grayout", "YUV only: don't write Cb and Cr, makes a nice effect", "", "0");
303 optstr_param(options, "hqconv", "YUV only: do high quality rgb->yuv img conversion", "", "0");
304 optstr_param(options, "flip", "Mirror image", "", "0");
305
306 return 0;
307 }
308
309 if (ptr->tag & TC_FILTER_INIT) {
310 Image *timg;
311 Image *nimg;
312 ImageInfo *image_info;
313 ExceptionInfo exception_info;
314
315 int rgb_off = 0;
316
317 vob_t *tmpvob;
318
319 tmpvob = tc_get_vob();
320 if (tmpvob == NULL)
321 return -1;
322 mfd_all[instance] = tc_zalloc(sizeof(MyFilterData));
323 if (mfd_all[instance] == NULL)
324 return -1;
325
326 mfd = mfd_all[instance];
327
328 strlcpy(mfd->file, "logo.png", PATH_MAX);
329 mfd->end = (unsigned int)-1;
330 mfd->vob = tmpvob;
331 vob = mfd->vob;
332
333 if (options != NULL) {
334 if (verbose)
335 tc_log_info(MOD_NAME, "options=%s", options);
336
337 optstr_get(options, "file", "%[^:]", mfd->file);
338 optstr_get(options, "posdef", "%d", (int *)&mfd->pos);
339 optstr_get(options, "pos", "%dx%d", &mfd->posx, &mfd->posy);
340 optstr_get(options, "range", "%u-%u", &mfd->start, &mfd->end);
341 optstr_get(options, "fade", "%u-%u", &mfd->fadein, &mfd->fadeout);
342
343 if (optstr_lookup(options, "ignoredelay") != NULL)
344 mfd->ignoredelay = !mfd->ignoredelay;
345 if (optstr_lookup(options, "flip") != NULL)
346 mfd->flip = !mfd->flip;
347 if (optstr_lookup(options, "rgbswap") != NULL)
348 mfd->rgbswap = !mfd->rgbswap;
349 if (optstr_lookup(options, "grayout") != NULL)
350 mfd->grayout = !mfd->grayout;
351 if (optstr_lookup(options, "hqconv") != NULL)
352 mfd->hqconv = !mfd->hqconv;
353
354 if (optstr_lookup (options, "help") != NULL)
355 flogo_help_optstr();
356 }
357
358 if (verbose > 1) {
359 tc_log_info(MOD_NAME, " Logo renderer Settings:");
360 tc_log_info(MOD_NAME, " file = %s", mfd->file);
361 tc_log_info(MOD_NAME, " posdef = %d", mfd->pos);
362 tc_log_info(MOD_NAME, " pos = %dx%d", mfd->posx,
363 mfd->posy);
364 tc_log_info(MOD_NAME, " range = %u-%u", mfd->start,
365 mfd->end);
366 tc_log_info(MOD_NAME, " fade = %u-%u", mfd->fadein,
367 mfd->fadeout);
368 tc_log_info(MOD_NAME, " flip = %d", mfd->flip);
369 tc_log_info(MOD_NAME, " ignoredelay = %d", mfd->ignoredelay);
370 tc_log_info(MOD_NAME, " rgbswap = %d", mfd->rgbswap);
371 tc_log_info(MOD_NAME, " grayout = %d", mfd->grayout);
372 tc_log_info(MOD_NAME, " hqconv = %d", mfd->hqconv);
373 }
374
375 /* Transcode serializes module execution, so this does not need a
376 * semaphore.
377 */
378 magick_usecount++;
379 if (!IsMagickInstantiated()) {
380 InitializeMagick("");
381 }
382
383 GetExceptionInfo(&exception_info);
384 image_info = CloneImageInfo((ImageInfo *) NULL);
385 strlcpy(image_info->filename, mfd->file, MaxTextExtent);
386
387 mfd->image = ReadImage(image_info, &exception_info);
388 if (mfd->image == (Image *) NULL) {
389 MagickWarning(exception_info.severity,
390 exception_info.reason,
391 exception_info.description);
392 strlcpy(mfd->file, "/dev/null", PATH_MAX);
393 return 0;
394 }
395 DestroyImageInfo(image_info);
396
397 if (mfd->image->columns > vob->ex_v_width
398 || mfd->image->rows > vob->ex_v_height
399 ) {
400 tc_log_error(MOD_NAME, "\"%s\" is too large", mfd->file);
401 return -1;
402 }
403
404 if (vob->im_v_codec == CODEC_YUV) {
405 if ((mfd->image->columns & 1) || (mfd->image->rows & 1)) {
406 tc_log_error(MOD_NAME, "\"%s\" has odd sizes", mfd->file);
407 return -1;
408 }
409 }
410
411 mfd->images = (Image *)GetFirstImageInList(mfd->image);
412 nimg = NewImageList();
413
414 while (mfd->images != (Image *)NULL) {
415 if (mfd->flip || flip) {
416 timg = FlipImage(mfd->images, &exception_info);
417 if (timg == (Image *) NULL) {
418 MagickError(exception_info.severity,
419 exception_info.reason,
420 exception_info.description);
421 return -1;
422 }
423 AppendImageToList(&nimg, timg);
424 }
425
426 mfd->images = GetNextImageInList(mfd->images);
427 mfd->nr_of_images++;
428 }
429
430 // check for memleaks;
431 //DestroyImageList(image);
432 if (mfd->flip || flip) {
433 mfd->image = nimg;
434 }
435
436 /* initial delay. real delay = 1/100 sec * delay */
437 mfd->cur_delay = mfd->image->delay*vob->fps/100;
438
439 if (verbose & TC_DEBUG)
440 tc_log_info(MOD_NAME, "Nr: %d Delay: %d mfd->image->del %lu|",
441 mfd->nr_of_images, mfd->cur_delay, mfd->image->delay);
442
443 if (vob->im_v_codec == CODEC_YUV) {
444 /* convert Magick RGB image format to YUV */
445 /* todo: convert the magick image if it's not rgb! (e.g. cmyk) */
446 Image *image;
447 uint8_t *yuv_hqbuf = NULL;
448
449 /* Round up for odd-size images */
450 unsigned long width = mfd->image->columns;
451 unsigned long height = mfd->image->rows;
452 int do_rgbswap = (rgbswap || mfd->rgbswap);
453 int i;
454
455 /* Allocate buffers for the YUV420P frames. mfd->nr_of_images
456 * will be 1 unless this is an animated GIF or MNG.
457 * This buffer needs to be large enough to store a temporary
458 * 24-bit RGB image (extracted from the ImageMagick handle).
459 */
460 mfd->yuv = flogo_yuvbuf_alloc(width*height * 3, mfd->nr_of_images);
461 if (mfd->yuv == NULL) {
462 tc_log_error(MOD_NAME, "(%d) out of memory\n", __LINE__);
463 return -1;
464 }
465
466 if (mfd->hqconv) {
467 /* One temporary buffer, to hold full Y, U, and V planes. */
468 yuv_hqbuf = tc_malloc(width*height * 3);
469 if (yuv_hqbuf == NULL) {
470 tc_log_error(MOD_NAME, "(%d) out of memory\n", __LINE__);
471 return -1;
472 }
473 }
474
475 mfd->tcvhandle = tcv_init();
476 if (mfd->tcvhandle == NULL) {
477 tc_log_error(MOD_NAME, "image conversion init failed");
478 return -1;
479 }
480
481 image = GetFirstImageInList(mfd->image);
482
483 for (i = 0; i < mfd->nr_of_images; i++) {
484 if (!mfd->hqconv) {
485 flogo_convert_image(mfd->tcvhandle, image, mfd->yuv[i],
486 IMG_YUV420P, do_rgbswap);
487 } else {
488 flogo_convert_image(mfd->tcvhandle, image, yuv_hqbuf,
489 IMG_YUV444P, do_rgbswap);
490
491 // Copy over Y data from the 444 image
492 ac_memcpy(mfd->yuv[i], yuv_hqbuf, width * height);
493
494 // Resize U plane by 1/2 in each dimension, into the
495 // mfd YUV buffer
496 tcv_zoom(mfd->tcvhandle,
497 yuv_hqbuf + (width * height),
498 mfd->yuv[i] + (width * height),
499 width,
500 height,
501 1,
502 width / 2,
503 height / 2,
504 TCV_ZOOM_LANCZOS3
505 );
506
507 // Do the same with the V plane
508 tcv_zoom(mfd->tcvhandle,
509 yuv_hqbuf + 2*width*height,
510 mfd->yuv[i] + width*height + (width/2)*(height/2),
511 width,
512 height,
513 1,
514 width / 2,
515 height / 2,
516 TCV_ZOOM_LANCZOS3
517 );
518 }
519 image = GetNextImageInList(image);
520 }
521
522 if (mfd->hqconv)
523 tc_free(yuv_hqbuf);
524
525 tcv_free(mfd->tcvhandle);
526 } else {
527 /* for RGB format is origin bottom left */
528 /* for RGB, rgbswap is done in the frame routine */
529 rgb_off = vob->ex_v_height - mfd->image->rows;
530 mfd->posy = rgb_off - mfd->posy;
531 }
532
533 switch (mfd->pos) {
534 case NONE: /* 0 */
535 break;
536 case TOP_LEFT:
537 mfd->posx = 0;
538 mfd->posy = rgb_off;
539 break;
540 case TOP_RIGHT:
541 mfd->posx = vob->ex_v_width - mfd->image->columns;
542 break;
543 case BOT_LEFT:
544 mfd->posy = vob->ex_v_height - mfd->image->rows - rgb_off;
545 break;
546 case BOT_RIGHT:
547 mfd->posx = vob->ex_v_width - mfd->image->columns;
548 mfd->posy = vob->ex_v_height - mfd->image->rows - rgb_off;
549 break;
550 case CENTER:
551 mfd->posx = (vob->ex_v_width - mfd->image->columns)/2;
552 mfd->posy = (vob->ex_v_height- mfd->image->rows)/2;
553 /* align to not cause color disruption */
554 if (mfd->posx & 1)
555 mfd->posx++;
556 if (mfd->posy & 1)
557 mfd->posy++;
558 break;
559 }
560
561
562 if (mfd->posy < 0 || mfd->posx < 0
563 || (mfd->posx + mfd->image->columns) > vob->ex_v_width
564 || (mfd->posy + mfd->image->rows) > vob->ex_v_height) {
565 tc_log_error(MOD_NAME, "invalid position");
566 return -1;
567 }
568
569 /* for running through image sequence */
570 mfd->images = mfd->image;
571
572
573 /* Set up image/video coefficient lookup tables */
574 if (img_coeff_lookup[0] < 0) {
575 int i;
576 float maxrgbval = (float)MaxRGB; // from ImageMagick
577
578 for (i = 0; i <= MAX_UINT8_VAL; i++) {
579 float x = (float)ScaleCharToQuantum(i);
580
581 /* Alternatively:
582 * img_coeff = (maxrgbval - x) / maxrgbval;
583 * vid_coeff = x / maxrgbval;
584 */
585 img_coeff_lookup[i] = 1.0 - (x / maxrgbval);
586 vid_coeff_lookup[i] = 1.0 - img_coeff_lookup[i];
587 }
588 }
589
590 // filter init ok.
591 if (verbose)
592 tc_log_info(MOD_NAME, "%s %s", MOD_VERSION, MOD_CAP);
593
594 return 0;
595 }
596
597
598 //----------------------------------
599 //
600 // filter close
601 //
602 //----------------------------------
603 if (ptr->tag & TC_FILTER_CLOSE) {
604 if (mfd) {
605 flogo_yuvbuf_free(mfd->yuv, mfd->nr_of_images);
606 mfd->yuv = NULL;
607
608 if (mfd->image) {
609 DestroyImage(mfd->image);
610 }
611
612 tc_free(mfd);
613 mfd = NULL;
614 mfd_all[instance] = NULL;
615 }
616
617 magick_usecount--;
618 if (magick_usecount == 0 && IsMagickInstantiated()) {
619 DestroyMagick();
620 }
621
622 return 0;
623 } /* filter close */
624
625
626 //----------------------------------
627 //
628 // filter frame routine
629 //
630 //----------------------------------
631
632
633 // tag variable indicates, if we are called before
634 // transcodes internal video/audo frame processing routines
635 // or after and determines video/audio context
636
637 if ((ptr->tag & TC_POST_M_PROCESS)
638 && (ptr->tag & TC_VIDEO)
639 && !(ptr->attributes & TC_FRAME_IS_SKIPPED)
640 ) {
641 PixelPacket *pixel_packet;
642 uint8_t *video_buf;
643
644 int do_fade = 0;
645 float fade_coeff = 0.0;
646 float img_coeff, vid_coeff;
647
648 /* Note: ImageMagick defines opacity = 0 as fully visible, and
649 * opacity = MaxRGB as fully transparent.
650 */
651 Quantum opacity;
652
653 int row, col;
654
655 if (ptr->id < mfd->start || ptr->id > mfd->end)
656 return 0;
657
658 if (strcmp(mfd->file, "/dev/null") == 0)
659 return 0;
660
661 if (ptr->id - mfd->start < mfd->fadein) {
662 // fading-in
663 fade_coeff = (float)(mfd->start - ptr->id + mfd->fadein) / (float)(mfd->fadein);
664 do_fade = 1;
665 } else if (mfd->end - ptr->id < mfd->fadeout) {
666 // fading-out
667 fade_coeff = (float)(ptr->id - mfd->end + mfd->fadeout) / (float)(mfd->fadeout);
668 do_fade = 1;
669 }
670
671 mfd->cur_delay--;
672
673 if (mfd->cur_delay < 0 || mfd->ignoredelay) {
674 int seq;
675
676 mfd->cur_seq = (mfd->cur_seq + 1) % mfd->nr_of_images;
677
678 mfd->images = mfd->image;
679 for (seq=0; seq<mfd->cur_seq; seq++)
680 mfd->images = mfd->images->next;
681
682 mfd->cur_delay = mfd->images->delay * vob->fps/100;
683 }
684
685 pixel_packet = GetImagePixels(mfd->images, 0, 0,
686 mfd->images->columns,
687 mfd->images->rows);
688
689 if (vob->im_v_codec == CODEC_RGB) {
690 unsigned long r_off, g_off, b_off;
691
692 if (!(rgbswap || mfd->rgbswap)) {
693 r_off = 0;
694 b_off = 2;
695 } else {
696 r_off = 2;
697 b_off = 0;
698 }
699 g_off = 1;
700
701 for (row = 0; row < mfd->image->rows; row++) {
702 video_buf = ptr->video_buf + 3 * ((row + mfd->posy) * vob->ex_v_width + mfd->posx);
703
704 for (col = 0; col < mfd->image->columns; col++) {
705 opacity = pixel_packet->opacity;
706
707 if (do_fade)
708 opacity += (Quantum)((MaxRGB - opacity) * fade_coeff);
709
710 if (opacity == 0) {
711 *(video_buf + r_off) = ScaleQuantumToChar(pixel_packet->red);
712 *(video_buf + g_off) = ScaleQuantumToChar(pixel_packet->green);
713 *(video_buf + b_off) = ScaleQuantumToChar(pixel_packet->blue);
714 } else if (opacity < MaxRGB) {
715 unsigned char opacity_uchar = ScaleQuantumToChar(opacity);
716 img_coeff = img_coeff_lookup[opacity_uchar];
717 vid_coeff = vid_coeff_lookup[opacity_uchar];
718
719 *(video_buf + r_off) = (uint8_t)((*(video_buf + r_off)) * vid_coeff)
720 + (uint8_t)(ScaleQuantumToChar(pixel_packet->red) * img_coeff);
721 *(video_buf + g_off) = (uint8_t)((*(video_buf + g_off)) * vid_coeff)
722 + (uint8_t)(ScaleQuantumToChar(pixel_packet->green) * img_coeff);
723 *(video_buf + b_off) = (uint8_t)((*(video_buf + b_off)) * vid_coeff)
724 + (uint8_t)(ScaleQuantumToChar(pixel_packet->blue) * img_coeff);
725 }
726
727 video_buf += 3;
728 pixel_packet++;
729 }
730 }
731 } else { /* !RGB */
732 unsigned long vid_size = vob->ex_v_width * vob->ex_v_height;
733 unsigned long img_size = mfd->images->columns * mfd->images->rows;
734
735 uint8_t *img_pixel_Y, *img_pixel_U, *img_pixel_V;
736 uint8_t *vid_pixel_Y, *vid_pixel_U, *vid_pixel_V;
737
738 img_pixel_Y = mfd->yuv[mfd->cur_seq];
739 img_pixel_U = img_pixel_Y + img_size;
740 img_pixel_V = img_pixel_U + img_size/4;
741
742 for (row = 0; row < mfd->images->rows; row++) {
743 vid_pixel_Y = ptr->video_buf + (row + mfd->posy)*mfd->vob->ex_v_width + mfd->posx;
744 vid_pixel_U = ptr->video_buf + vid_size + (row/2 + mfd->posy/2)*(mfd->vob->ex_v_width/2) + mfd->posx/2;
745 vid_pixel_V = vid_pixel_U + vid_size/4;
746 for (col = 0; col < mfd->images->columns; col++) {
747 int do_UV_pixels = (mfd->grayout == 0 && !(row % 2) && !(col % 2)) ? 1 : 0;
748 opacity = pixel_packet->opacity;
749
750 if (do_fade)
751 opacity += (Quantum)((MaxRGB - opacity) * fade_coeff);
752
753 if (opacity == 0) {
754 *vid_pixel_Y = *img_pixel_Y;
755 if (do_UV_pixels) {
756 *vid_pixel_U = *img_pixel_U;
757 *vid_pixel_V = *img_pixel_V;
758 }
759 } else if (opacity < MaxRGB) {
760 unsigned char opacity_uchar = ScaleQuantumToChar(opacity);
761 img_coeff = img_coeff_lookup[opacity_uchar];
762 vid_coeff = vid_coeff_lookup[opacity_uchar];
763
764 *vid_pixel_Y = (uint8_t)(*vid_pixel_Y * vid_coeff) + (uint8_t)(*img_pixel_Y * img_coeff);
765
766 if (do_UV_pixels) {
767 *vid_pixel_U = (uint8_t)(*vid_pixel_U * vid_coeff) + (uint8_t)(*img_pixel_U * img_coeff);
768 *vid_pixel_V = (uint8_t)(*vid_pixel_V * vid_coeff) + (uint8_t)(*img_pixel_V * img_coeff);
769 }
770 }
771
772 vid_pixel_Y++;
773 img_pixel_Y++;
774 if (do_UV_pixels) {
775 vid_pixel_U++;
776 img_pixel_U++;
777 vid_pixel_V++;
778 img_pixel_V++;
779 }
780 pixel_packet++;
781 }
782 }
783 }
784 }
785
786 return 0;
787 }
788
789 /*************************************************************************/
790
791 /*
792 * Local variables:
793 * c-file-style: "stroustrup"
794 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
795 * indent-tabs-mode: nil
796 * End:
797 *
798 * vim: expandtab shiftwidth=4:
799 */
800