Bug Summary

File:subgen-image.c
Location:line 747, column 17
Description:Branch condition evaluates to a garbage value

Annotated Source Code

1/*
2 Reading of subpicture images and handling of buttons and associated palettes
3*/
4/*
5 * Copyright (C) 2002 Scott Smith (trckjunky@users.sourceforge.net)
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or (at
10 * your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 * MA 02110-1301 USA.
21 */
22
23#include "config.h"
24
25#include "compat.h"
26
27#include <assert.h>
28#include <fcntl.h>
29#include <math.h>
30
31#if defined(HAVE_MAGICK) || defined(HAVE_GMAGICK1)
32#include <stdarg.h>
33#include <magick/api.h>
34#else
35#include <png.h>
36#endif
37
38#include "subglobals.h"
39#include "subrender.h"
40#include "subgen.h"
41
42
43#define MAXX720 720
44#define MAXY576 576
45
46bool_Bool text_forceit = false0; /* Forcing of the subtitles */
47sub_data *textsub_subdata;
48
49static void constructblankpic(pict *p,int w,int h)
50 /* allocates and fills in p with an image consisting entirely of transparent pixels */
51{
52 w+=w&1;
53 h+=h&1;
54 p->width=w;
55 p->height=h;
56 p->fname=0;
57 p->img=malloc(w*h);
58 p->numpal=1;
59 p->pal[0].r=0;
60 p->pal[0].g=0;
61 p->pal[0].b=0;
62 p->pal[0].a=0;
63 memset(p->img,0,w*h);
64}
65
66typedef struct { /* a set of colours in a subpicture or button */
67 int numpal; /* nr entries used in pal */
68 int pal[4]; /* that's all the DVD-video spec allows */
69} palgroup;
70
71// ****************************************************
72//
73// Bitmap reading code
74
75// this function scans through a picture and looks for the color that
76// the user indicated should mean transparent
77// if the color is found, it is replaced by 0,0,0,0 meaning "transparent"
78static void scanpict(stinfo *s, pict *p)
79 {
80 int i;
81 // we won't replace the background color if there is already evidence
82 // of alpha channel information
83 for (i = 0; i < p->numpal; i++)
84 if (p->pal[i].a != 255)
85 goto skip_transreplace;
86 for (i = 0; i < p->numpal; i++)
87 if (!memcmp(p->pal + i, &s->transparentc, sizeof(colorspec)))
88 { /* found matching entry */
89 memset(&p->pal[i], 0, sizeof(colorspec)); /* zap the RGB components */
90 break;
91 } /*if; for*/
92 skip_transreplace:
93 for (i = 0; i < p->numpal; i++)
94 (void)findmasterpal(s, p->pal + i);
95 /* just make sure all the colours are in the colour table */
96 } /*scanpict*/
97
98static void putpixel(pict *p, int x, const colorspec *c)
99 /* stores another pixel into pict p at offset x with colour c. Adds a new
100 entry into the colour table if not already present and there's room. */
101 {
102 int i;
103 colorspec ct;
104 if (!c->a && (c->r || c->g || c->b))
105 {
106 /* all transparent pixels look alike to me */
107 ct.a = 0;
108 ct.r = 0;
109 ct.g = 0;
110 ct.b = 0;
111 c = &ct;
112 } /*if*/
113 for (i = 0; i < p->numpal; i++)
114 if (!memcmp(&p->pal[i], c, sizeof(colorspec)))
115 {
116 /* matches existing palette entry */
117 p->img[x] = i;
118 return;
119 } /*if; for*/
120 if (p->numpal == 256)
121 {
122 /* too many colours */
123 p->img[x] = 0;
124 return;
125 } /*if*/
126 /* allocate new palette entry */
127 p->img[x] = p->numpal;
128/* fprintf(stderr, "CREATING COLOR %d,%d,%d %d\n", c->r, c->g, c->b, c->a); */
129 p->pal[p->numpal++] = *c;
130 } /*putpixel*/
131
132static void createimage(pict *s, int w, int h)
133 /* allocates memory for pixels in s with dimensions w and h. */
134 {
135 s->numpal = 0;
136 /* ensure allocated dimensions are even */
137 s->width = w + (w & 1);
138 s->height = h + (h & 1);
139 s->img = malloc(s->width * s->height);
140 if (w != s->width)
141 {
142 /* set padding pixels along side to transparent */
143 int y;
144 colorspec t;
145 t.r = 0;
146 t.g = 0;
147 t.b = 0;
148 t.a = 0;
149 for (y = 0; y < h; y++)
150 putpixel(s, w + y * s->width, &t);
151 } /*if*/
152 if (h != s->height)
153 {
154 /* set padding pixels along bottom to transparent */
155 int x;
156 colorspec t;
157 t.r = 0;
158 t.g = 0;
159 t.b = 0;
160 t.a = 0;
161 for (x = 0; x < s->width; x++)
162 putpixel(s, x + h * s->width, &t);
163 } /*if*/
164 } /*createimage*/
165
166#if defined(HAVE_MAGICK) || defined(HAVE_GMAGICK1)
167// meaning of A in RGBA swapped in ImageMagick 6.0.0 and GraphicsMagick 1.3.8
168#if defined(HAVE_MAGICK)
169#define XMAGICK_NEW_RGBA_MINVER0x060300 0x600
170#else // HAVE_GMAGICK
171#define XMAGICK_NEW_RGBA_MINVER0x060300 0x060300
172#define ExportImagePixelsDispatchImage DispatchImage
173#endif
174static int read_magick(pict *s)
175/* uses ImageMagick/GraphicsMagick to read image s from s->fname. */
176{
177 Image *im;
178 ImageInfo *ii;
179 ExceptionInfo ei;
180 int x,y;
181 unsigned long magickver;
182 unsigned char amask;
183
184 GetExceptionInfo(&ei);
185 ii=CloneImageInfo(NULL((void*)0));
186 strcpy(ii->filename,s->fname);
187 im=ReadImage(ii,&ei);
188
189 if( !im ) {
190 MagickError(ei.severity,"Unable to load file",ii->filename);
191 return -1;
192 }
193
194 if( im->columns>MAXX720 || im->rows>MAXY576 ) {
195 fprintf(stderrstderr,"ERR: Picture %s is too big: %lux%lu\n",s->fname,im->columns,im->rows);
196 DestroyImage(im);
197 return -1;
198 }
199 createimage(s,im->columns,im->rows);
200 GetMagickVersion(&magickver);
201 amask = magickver < XMAGICK_NEW_RGBA_MINVER0x060300 ? 255 : 0;
202 for( y=0; y<im->rows; y++ ) {
203 char pdata[MAXX720*4];
204
205 if(!ExportImagePixelsDispatchImage(im,0,y,im->columns,1,"RGBA",CharPixel,pdata,&ei)) {
206 fprintf(stderrstderr,"ERR: Extracting row %d from %s (%s,%s)\n",y,s->fname,ei.reason,ei.description);
207 CatchException(&ei);
208 MagickError(ei.severity,ei.reason,ei.description);
209 DestroyImage(im);
210 return -1;
211 }
212 for( x=0; x<im->columns; x++ ) {
213 colorspec p;
214 p.r=pdata[x*4];
215 p.g=pdata[x*4+1];
216 p.b=pdata[x*4+2];
217 p.a = pdata[x*4+3] ^ amask;
218 putpixel(s,y*s->width+x,&p);
219 }
220 }
221 DestroyImage(im);
222 DestroyExceptionInfo(&ei);
223 fprintf(stderrstderr,"INFO: Picture %s had %d colors\n",s->fname,s->numpal);
224
225 return 0;
226}
227#else
228static int read_png(pict *s)
229/* uses libpng to read image s from s->fname. */
230{
231 unsigned char pnghead[8];
232 FILE *fp;
233 png_struct *ps;
234 png_info *pi;
235 png_byte **rowp;
236 png_uint_32 width,height;
237 int bit_depth,color_type,channels,x,y;
238
239 fp=fopen(s->fname,"rb");
240 if( !fp ) {
241 fprintf(stderrstderr,"ERR: Unable to open file %s\n",s->fname);
242 return -1;
243 }
244 fread(pnghead,1,8,fp);
245 if(png_sig_cmp(pnghead,0,8)) {
246 fprintf(stderrstderr,"ERR: File %s isn't a png\n",s->fname);
247 fclose(fp);
248 return -1;
249 }
250 ps=png_create_read_struct(PNG_LIBPNG_VER_STRING,NULL((void*)0),NULL((void*)0),NULL((void*)0));
251 if( !ps ) {
252 fprintf(stderrstderr,"ERR: Initializing png\n");
253 fclose(fp);
254 return -1;
255 }
256 pi=png_create_info_struct(ps);
257 if( !pi ) {
258 fprintf(stderrstderr,"ERR: Initializing png\n");
259 png_destroy_read_struct(&ps,NULL((void*)0),NULL((void*)0));
260 fclose(fp);
261 return -1;
262 }
263 png_init_io(ps,fp);
264 png_set_sig_bytes(ps,8);
265
266 png_read_png(ps,pi,PNG_TRANSFORM_PACKING|PNG_TRANSFORM_STRIP_16|PNG_TRANSFORM_EXPAND,NULL((void*)0));
267 rowp=png_get_rows(ps,pi);
268 png_get_IHDR(ps,pi,&width,&height,&bit_depth,&color_type,NULL((void*)0),NULL((void*)0),NULL((void*)0));
269 // format is now RGB[A] or G[A]
270 channels=png_get_channels(ps,pi);
271 fclose(fp);
272 if(color_type&PNG_COLOR_MASK_COLOR)
273 channels-=3;
274 else
275 channels--;
276 if(color_type&PNG_COLOR_MASK_ALPHA)
277 channels--;
278 assert(bit_depth==8)((bit_depth==8) ? (void) (0) : __assert_fail ("bit_depth==8",
"subgen-image.c", 278, __PRETTY_FUNCTION__))
; // 8bpp, not 1, 2, 4, or 16
279 assert(!(color_type&PNG_COLOR_MASK_PALETTE))((!(color_type&PNG_COLOR_MASK_PALETTE)) ? (void) (0) : __assert_fail
("!(color_type&PNG_COLOR_MASK_PALETTE)", "subgen-image.c"
, 279, __PRETTY_FUNCTION__))
; // not a palette
280 if( width>MAXX720 || height>MAXY576 ) {
281 fprintf(stderrstderr,"ERR: PNG %s is too big: %lux%lu\n",s->fname,width,height);
282 png_destroy_read_struct(&ps,&pi,NULL((void*)0));
283 return -1;
284 }
285 createimage(s,width,height);
286 for( y=0; y<height; y++ ) {
287 unsigned char *d=rowp[y];
288 for( x=0; x<width; x++ ) {
289 colorspec p;
290 if(color_type&PNG_COLOR_MASK_COLOR) {
291 p.r=*d++;
292 p.g=*d++;
293 p.b=*d++;
294 } else {
295 p.r=*d++;
296 p.g=p.r;
297 p.b=p.r;
298 }
299 if( color_type&PNG_COLOR_MASK_ALPHA )
300 p.a=*d++;
301 else
302 p.a=255;
303 d+=channels;
304 putpixel(s,y*s->width+x,&p);
305 }
306 // free(rowp[y]);
307 }
308 // free(rowp);
309 png_destroy_read_struct(&ps,&pi,NULL((void*)0));
310 fprintf(stderrstderr,"INFO: PNG had %d colors\n",s->numpal);
311
312 return 0;
313}
314#endif
315
316static int read_frame(pict *s)
317 /* fills in s from textsub_image_buffer. */
318 {
319 int x, y;
320 createimage(s, movie_width, movie_height);
321 for (y = 0; y < movie_height; y++)
322 {
323 const unsigned char * d = textsub_image_buffer + y * movie_width * 4;
324 for (x = 0; x < movie_width; x++)
325 {
326 colorspec p;
327 p.r = *d++;
328 p.g = *d++;
329 p.b = *d++;
330 p.a = *d++;
331 putpixel(s, y * s->width + x, &p);
332 } /*for*/
333 } /*for*/
334 return 0;
335 } /*read_frame*/
336
337static int read_pic(stinfo *s, pict *p)
338 /* gets the image(s) specified by p into s. */
339 {
340 int r = 0;
341 if (have_textsub)
342 {
343 vo_update_osd(s->sub_title); /* will allocate and render into textsub_image_buffer */
344 s->forced = text_forceit;
345 r = read_frame(p);
346 }
347 else /* read image file */
348 {
349 if (!p->fname)
350 return 0;
351#if defined(HAVE_MAGICK) || defined(HAVE_GMAGICK1)
352 r = read_magick(p);
353#else
354 r = read_png(p);
355#endif
356 } /*if*/
357 if (!r)
358 scanpict(s, p);
359 return r;
360 } /*read_pic*/
361
362static bool_Bool checkcolor(palgroup *p,int c)
363 /* tries to put colour c into palette p, returning true iff successful or already there. */
364{
365 int i;
366 for( i=0; i<p->numpal; i++ )
367 if( p->pal[i]==c )
368 return true1;
369 if( p->numpal==4 )
370 return false0;
371 p->pal[p->numpal++]=c;
372 return true1;
373}
374
375static int gettricolor(stinfo *s,int p,int useimg)
376 /* returns an index used to represent the particular combination of colours used
377 in s->img, s->hlt and s->sel at offset p. */
378 {
379 return
380 (useimg ? s->img.img[p] << 16 : 0)
381 |
382 s->hlt.img[p] << 8
383 |
384 s->sel.img[p];
385 } /*gettricolor*/
386
387static bool_Bool pickbuttongroups(stinfo *s, int ng, int useimg)
388 /* tries to assign the buttons in s to ng unique groups. useimg indicates
389 whether to look at the pixels in s->img in addition to s->hlt and s->sel. */
390 {
391 palgroup *bpgs, *gs;
392 int i, x, y, j, k, enb;
393
394 bpgs = malloc(s->numbuttons * sizeof(palgroup)); /* colour tables for each button */
395 memset(bpgs, 0, s->numbuttons * sizeof(palgroup));
396
397 gs = malloc(ng * sizeof(palgroup)); /* colour tables for each button group */
398 memset(gs, 0, ng * sizeof(palgroup));
399
400 assert(!useimg || (s->xd <= s->img.width && s->yd <= s->img.height))((!useimg || (s->xd <= s->img.width && s->
yd <= s->img.height)) ? (void) (0) : __assert_fail ("!useimg || (s->xd <= s->img.width && s->yd <= s->img.height)"
, "subgen-image.c", 400, __PRETTY_FUNCTION__))
;
401 assert(s->xd <= s->hlt.width && s->yd <= s->hlt.height)((s->xd <= s->hlt.width && s->yd <= s->
hlt.height) ? (void) (0) : __assert_fail ("s->xd <= s->hlt.width && s->yd <= s->hlt.height"
, "subgen-image.c", 401, __PRETTY_FUNCTION__))
;
402 assert(s->xd <= s->sel.width && s->yd <= s->sel.height)((s->xd <= s->sel.width && s->yd <= s->
sel.height) ? (void) (0) : __assert_fail ("s->xd <= s->sel.width && s->yd <= s->sel.height"
, "subgen-image.c", 402, __PRETTY_FUNCTION__))
;
403
404 // fprintf(stderr,"attempt %d groups, %d useimg\n",ng,useimg);
405 // find unique colors per button
406 for (i = 0; i < s->numbuttons; i++)
407 {
408 /* check coordinates of buttons make sense, and determine their colour tables */
409 button *b = &s->buttons[i];
410 palgroup *bp = &bpgs[i]; /* button's combined colour table for all images */
411
412 if
413 (
414 b->r.x0 != b->r.x1
415 &&
416 b->r.y0 != b->r.y1
417 &&
418 (
419 b->r.x0 < 0
420 ||
421 b->r.x0 > b->r.x1
422 ||
423 b->r.x1 > s->xd
424 ||
425 b->r.y0 < 0
426 ||
427 b->r.y0 > b->r.y1
428 ||
429 b->r.y1 > s->yd
430 )
431 )
432 {
433 if (debug > -1)
434 fprintf
435 (
436 stderrstderr,
437 "ERR: Button coordinates out of range (%d,%d): (%d,%d)-(%d,%d)\n",
438 s->xd, s->yd,
439 b->r.x0, b->r.y0, b->r.x1, b->r.y1
440 );
441 exit(1);
442 } /*if*/
443 for (y = b->r.y0; y < b->r.y1; y++)
444 for (x = b->r.x0; x < b->r.x1; x++)
445 if (!checkcolor(bp, gettricolor(s, y * s->xd + x, useimg)))
446 goto impossible;
447 // fprintf(stderr, "pbg: button %d has %d colors\n", i, bp->numpal);
448 } /*for i*/
449
450 // assign to groups
451 enb = 1;
452 for (i = 0; i < s->numbuttons; i++)
453 enb *= ng; /* how many combinations to try--warning! combinatorial explosion! */
454 for (i = 0; i < enb; i++)
455 {
456 /* try another combination for assigning buttons to groups */
457 for (j = 0; j < ng; j++)
458 gs[j].numpal = 0; /* clear all button group palettes for new attempt */
459 // fprintf(stderr, "trying ");
460 k = i; /* combinator */
461 for (j = 0; j < s->numbuttons; j++)
462 {
463 /* assemble this combination of merging button palettes into group palettes */
464 int l;
465 palgroup *pd = &gs[k % ng];
466 /* palette for group to which to try to assign this button */
467 palgroup *ps = &bpgs[j]; /* palette for button */
468
469 s->buttons[j].grp = k % ng + 1; /* assign button group */
470 // fprintf(stderr, "%s%d",j?", ":"", s->buttons[j].grp);
471 k /= ng;
472 for (l = 0; l < ps->numpal; l++)
473 /* try to merge button palette into group palette */
474 if (!checkcolor(pd, ps->pal[l]))
475 {
476 // fprintf(stderr, " -- failed mapping button %d\n", j);
477 goto trynext;
478 } /*if; for*/
479 } /*for j*/
480 if (useimg)
481 {
482 /* save the final button group palettes, ensuring the colours used in s->img
483 for all the buttons fit in a single palette */
484 palgroup p;
485
486 p.numpal = s->img.numpal;
487 for (j = 0; j < p.numpal; j++)
488 p.pal[j] = j;
489 for (j = 0; j < ng; j++)
490 {
491 for (k = 0; k < 4; k++)
492 s->groupmap[j][k] = -1; /* button group palette initially empty */
493 for (k = 0; k < p.numpal; k++)
494 p.pal[k] &= 255; /* clear colour-already-matched flag */
495 for (k = 0; k < gs[j].numpal; k++)
496 {
497 int l, c;
498
499 c = gs[j].pal[k] >> 16; /* s->img component of tricolor from button group palette */
500 for (l = 0; l < p.numpal; l++)
501 if (p.pal[l] == c)
502 {
503 goto ui_found;
504 } /*if; for */
505 if (p.numpal == 4)
506 {
507 // fprintf(stderr, " -- failed finding unique overall palette\n");
508 goto trynext;
509 } /*if*/
510 p.numpal++;
511ui_found:
512 p.pal[l] = c | 256; /* mark palette entry as already matched */
513 s->groupmap[j][l] = gs[j].pal[k]; /* merge colour into button group palette */
514 } /*for*/
515 } /*for j*/
516 }
517 else
518 {
519 /* save the final button group palettes */
520 for (j = 0; j < ng; j++)
521 {
522 for (k = 0; k < gs[j].numpal; k++)
523 s->groupmap[j][k] = gs[j].pal[k];
524 for (; k < 4; k++)
525 s->groupmap[j][k] = -1; /* unused palette entries */
526 } /*for*/
527 } /*if*/
528 free(bpgs);
529 free(gs);
530
531 // If possible, make each palette entry 0 transparent in
532 // all states, since some players may pad buttons with 0
533 // and we also pad with 0 in some cases.
534 for (j = 0; j < ng; j++)
535 {
536 int spare = -1;
537 int tri; /* tricolor */
538
539 // search for an unused color, or one that is already fully transparent
540 for (k = 0; k < 4; k++)
541 {
542 tri = s->groupmap[j][k];
543 if
544 (
545 tri == -1
546 ||
547 s->img.pal[(tri >> 16) & 0xFF].a == 0
548 &&
549 s->hlt.pal[(tri >> 8) & 0xFF].a == 0
550 &&
551 s->sel.pal[tri & 0xFF].a == 0
552 )
553 {
554 spare = k;
555 break;
556 } /*if*/
557 } /*for*/
558 /* at this point, if spare = 0 then nothing to do, entry if any at location 0
559 is already transparent */
560 if (spare > 0 && tri == -1)
561 {
562 /* got an unused slot, make up a transparent entry to exchange it with */
563 tri = 0;
564 for (k = 0; k < s->img.numpal; ++k)
565 if (s->img.pal[k].a == 0)
566 {
567 tri |= k << 16;
568 break;
569 } /*if; for*/
570 for (k = 0; k < s->hlt.numpal; ++k)
571 if (s->hlt.pal[k].a == 0)
572 {
573 tri |= k << 8;
574 break;
575 } /*if; for*/
576 for (k = 0; k < s->sel.numpal; ++k)
577 if (s->sel.pal[k].a == 0)
578 {
579 tri |= k;
580 break;
581 } /*if*/
582 } /*if*/
583 if (spare > 0)
584 {
585 /* move transparent colour to location 0 */
586 s->groupmap[j][spare] = s->groupmap[j][0]; /* move nontransparent colour */
587 s->groupmap[j][0] = tri; /* to make way for transparent one */
588 } /*if*/
589 } /*for j*/
590
591 fprintf(stderrstderr, "INFO: Pickbuttongroups, success with %d groups, useimg=%d\n", ng, useimg);
592 s->numgroups = ng;
593 return true1;
594trynext:
595 continue; // 'deprecated use of label at end of compound statement'
596 } /*for i*/
597
598impossible:
599 free(bpgs);
600 free(gs);
601 return false0;
602 } /*pickbuttongroups*/
603
604static void fixnames(stinfo *s)
605 /* assigns default names to buttons that don't already have them. */
606 {
607 int i;
608 for (i = 0; i < s->numbuttons; i++)
609 if (!s->buttons[i].name)
610 {
611 char n[10]; /* should be enough! */
612 sprintf(n, "%d", i + 1);
613 s->buttons[i].name = strdup(n)(__extension__ (__builtin_constant_p (n) && ((size_t)
(const void *)((n) + 1) - (size_t)(const void *)(n) == 1) ? (
((const char *) (n))[0] == '\0' ? (char *) calloc ((size_t) 1
, (size_t) 1) : ({ size_t __len = strlen (n) + 1; char *__retval
= (char *) malloc (__len); if (__retval != ((void*)0)) __retval
= (char *) memcpy (__retval, n, __len); __retval; })) : __strdup
(n)))
;
614 } /*if; for*/
615 } /*fixnames*/
616
617// a0 .. a1, b0 .. b1 are analogous to y coordinates, positive -> high, negative->low
618// d is the distance from a to b (assuming b is to the right of a, i.e. positive x)
619// returns angle (0 = straight right, 90 = straight up, -90 = straight down) from a to b
620static void brphelp(int a0, int a1, int b0, int b1, int d, int *angle, int *dist)
621 {
622 int d1, d2, m;
623 if (a1 > b0 && a0 < b1)
624 {
625 *angle = 0;
626 *dist = d;
627 return;
628 } /*if*/
629 d1 = -(b0 - a1 + 1);
630 d2 = (a0 - b1 + 1);
631 d++;
632 if (abs(d2) <abs(d1))
633 {
634 d1 = d2;
635 } /*if*/
636 m = 1;
637 if (d1 < 0)
638 {
639 d1 = -d1;
640 m = -1;
641 } /*if*/
642 *angle = m * 180 / M_PI3.14159265358979323846 * atan(((double)d1) / ((double)d));
643 *dist = sqrt(d1 * d1+d * d);
644 } /*brphelp*/
645
646static bool_Bool buttonrelpos(const rectangle *a, const rectangle *b, int *angle, int *dist)
647 /* computes an angle and distance from the position of rectangle a to that of rectangle b,
648 if I can figure one out. Returns true if the case is sufficiently simple for me to handle,
649 false otherwise. */
650 {
651 // from b to a
652 if (a->y1 <= b->y0)
653 {
654 /* a lies above b with no vertical overlap */
655 brphelp(a->x0, a->x1, b->x0, b->x1, b->y0 - a->y1, angle, dist);
656 // fprintf(stderr,"from %dx%d-%dx%d to %dx%d-%dx%d is angle %d, dist %d\n",b->x0,b->y0,b->x1,b->y1,a->x0,a->y0,a->x1,a->y1,*angle,*dist);
657 return true1;
658 } /*if*/
659 if (a->y0 >= b->y1)
660 {
661 /* a lies below b with no vertical overlap */
662 brphelp(a->x0, a->x1, b->x0, b->x1, a->y0 - b->y1, angle, dist);
663 *angle = 180 - *angle;
664 // fprintf(stderr,"from %dx%d-%dx%d to %dx%d-%dx%d is angle %d, dist %d\n",b->x0,b->y0,b->x1,b->y1,a->x0,a->y0,a->x1,a->y1,*angle,*dist);
665 return true1;
666 } /*if*/
667 if (a->x1 <= b->x0)
668 {
669 /* a lies to the left of b with no horizontal overlap */
670 brphelp(a->y0, a->y1, b->y0, b->y1, b->x0 - a->x1, angle, dist);
671 *angle = 270 - *angle;
672 // fprintf(stderr,"from %dx%d-%dx%d to %dx%d-%dx%d is angle %d, dist %d\n",b->x0,b->y0,b->x1,b->y1,a->x0,a->y0,a->x1,a->y1,*angle,*dist);
673 return true1;
674 } /*if*/
675 if (a->x0 >= b->x1)
676 {
677 /* a lies to the right of b with no horizontal overlap */
678 brphelp(a->y0, a->y1, b->y0, b->y1, a->x0 - b->x1, angle, dist);
679 *angle = 90 + *angle;
680 // fprintf(stderr,"from %dx%d-%dx%d to %dx%d-%dx%d is angle %d, dist %d\n",b->x0,b->y0,b->x1,b->y1,a->x0,a->y0,a->x1,a->y1,*angle,*dist);
681 return true1;
682 } /*if*/
683 /* none of the above */
684 // fprintf(stderr,"from %dx%d-%dx%d to %dx%d-%dx%d -- no easy comparison\n",b->x0,b->y0,b->x1,b->y1,a->x0,a->y0,a->x1,a->y1);
685 return false0;
686 } /*buttonrelpos*/
687
688static void findbestbindir(stinfo *s, const button *b, char **dest, int a)
689 /* finds the best button to go to in the specified direction and returns its
690 name in *dest, if that has not already been set. */
691 {
692 int i, la = 0, ld = 0;
693 if (*dest) /* already got one */
694 return;
695 // fprintf(stderr,"locating nearest button from %s, angle %d\n",b->name,a);
696 for (i = 0; i < s->numbuttons; i++)
697 if (b != &s->buttons[i])
698 {
699 int na, nd;
700 if (buttonrelpos(&s->buttons[i].r, &b->r, &na, &nd))
701 {
702 na = abs(na - a); /* error from desired direction */
703 if (na >= 90) /* completely wrong direction */
704 continue;
705 if (!*dest || na < la || (na == la && nd < ld))
706 {
707 /* first candidate, or better score than previous candidate */
708 // fprintf(stderr,"\tchoosing %s, na=%d, d=%d\n",s->buttons[i].name,na,nd);
709 *dest = s->buttons[i].name;
710 la = na;
711 ld = nd;
712 } /*if*/
713 } /*if*/
714 } /*if; for*/
715 if (*dest)
716 *dest = strdup(*dest)(__extension__ (__builtin_constant_p (*dest) && ((size_t
)(const void *)((*dest) + 1) - (size_t)(const void *)(*dest) ==
1) ? (((const char *) (*dest))[0] == '\0' ? (char *) calloc (
(size_t) 1, (size_t) 1) : ({ size_t __len = strlen (*dest) + 1
; char *__retval = (char *) malloc (__len); if (__retval != (
(void*)0)) __retval = (char *) memcpy (__retval, *dest, __len
); __retval; })) : __strdup (*dest)))
; /* copy the name I found */
717 else
718 *dest = strdup(b->name)(__extension__ (__builtin_constant_p (b->name) && (
(size_t)(const void *)((b->name) + 1) - (size_t)(const void
*)(b->name) == 1) ? (((const char *) (b->name))[0] == '\0'
? (char *) calloc ((size_t) 1, (size_t) 1) : ({ size_t __len
= strlen (b->name) + 1; char *__retval = (char *) malloc (
__len); if (__retval != ((void*)0)) __retval = (char *) memcpy
(__retval, b->name, __len); __retval; })) : __strdup (b->
name)))
; /* back to same button in this direction */
719 } /*findbestbindir*/
720
721static void detectdirections(stinfo *s)
722 /* automatically detects the neighbours of each button in each direction, where
723 these have not already been specified. */
724 {
725 int i;
726 for (i = 0; i < s->numbuttons; i++)
727 {
728 findbestbindir(s, &s->buttons[i], &s->buttons[i].up, 0);
729 findbestbindir(s, &s->buttons[i], &s->buttons[i].down, 180);
730 findbestbindir(s, &s->buttons[i], &s->buttons[i].left, 270);
731 findbestbindir(s, &s->buttons[i], &s->buttons[i].right, 90);
732 } /*for*/
733 } /*detectdirections*/
734
735#define MAX(a,b)(((a)>(b))?(a):(b)) (((a)>(b))?(a):(b))
736#define MIN(a,b)(((a)<(b))?(a):(b)) (((a)<(b))?(a):(b))
737
738static bool_Bool scanvline(stinfo *s,unsigned char *v,rectangle *r,int x,int d)
739{
740 int i,j;
741
742 for( j=1; j<=s->outlinewidth; j++ ) {
19
Loop condition is true. Entering loop body
743 x+=d;
744 if( x<0 || x>=s->xd )
20
Taking false branch
745 return false0;
746 for( i=MAX(r->y0-j,0)(((r->y0-j)>(0))?(r->y0-j):(0)); i<MIN(r->y1+j,s->yd)(((r->y1+j)<(s->yd))?(r->y1+j):(s->yd)); i++ )
21
Loop condition is true. Entering loop body
23
Loop condition is true. Entering loop body
747 if( v[i*s->xd+x] )
22
Taking false branch
24
Branch condition evaluates to a garbage value
748 return true1;
749 }
750 return false0;
751}
752
753static bool_Bool scanhline(stinfo *s,unsigned char *v,rectangle *r,int y,int d)
754{
755 int i,j;
756
757 for( j=1; j<=s->outlinewidth; j++ ) {
758 y+=d;
759 if( y<0 || y>=s->yd )
760 return false0;
761 for( i=MAX(r->x0-j,0)(((r->x0-j)>(0))?(r->x0-j):(0)); i<MIN(r->x1+j,s->xd)(((r->x1+j)<(s->xd))?(r->x1+j):(s->xd)); i++ )
762 if( v[y*s->xd+i] )
763 return true1;
764 }
765 return false0;
766}
767
768static void detectbuttons(stinfo *s)
769 /* does automatic detection of button outlines. */
770{
771 unsigned char *visitmask=malloc(s->xd*s->yd);
772 int i,x,y;
773 rectangle *rs=0;
774 int numr=0;
775
776 if( !s->outlinewidth )
1
Taking false branch
777 s->outlinewidth=1;
778 for( i=0; i<s->xd*s->yd; i++ )
2
Loop condition is true. Entering loop body
4
Loop condition is true. Entering loop body
6
Loop condition is true. Entering loop body
7
Loop condition is false. Execution continues on line 780
779 visitmask[i]=( s->hlt.pal[s->hlt.img[i]].a || s->sel.pal[s->sel.img[i]].a ) ? 1 : 0;
3
'?' condition is false
5
'?' condition is false
780 for( y=0; y<s->yd; y++ )
8
Loop condition is true. Entering loop body
14
Loop condition is true. Entering loop body
781 for( x=0; x<s->xd; x++ )
9
Loop condition is true. Entering loop body
11
Loop condition is true. Entering loop body
13
Loop condition is false. Execution continues on line 780
15
Loop condition is true. Entering loop body
782 if( visitmask[y*s->xd+x] ) {
10
Taking false branch
12
Taking false branch
16
Taking true branch
783 rectangle r;
784 bool_Bool didwork;
785
786 r.x0=x;
787 r.y0=y;
788 r.x1=x+1;
789 r.y1=y+1;
790
791 do {
792 didwork = false0;
793 while( scanvline(s,visitmask,&r,r.x0,-1) ) {
17
Loop condition is false. Execution continues on line 797
794 r.x0--;
795 didwork = true1;
796 }
797 while( scanvline(s,visitmask,&r,r.x1-1,1) ) {
18
Calling 'scanvline'
798 r.x1++;
799 didwork = true1;
800 }
801 while( scanhline(s,visitmask,&r,r.y0,-1) ) {
802 r.y0--;
803 didwork = true1;
804 }
805 while( scanhline(s,visitmask,&r,r.y1-1,1) ) {
806 r.y1++;
807 didwork = true1;
808 }
809 } while(didwork);
810
811 r.y0-=r.y0&1; // buttons need even 'y' coordinates
812 r.y1+=r.y1&1;
813
814 // add button r
815 rs=realloc(rs,(numr+1)*sizeof(rectangle));
816 rs[numr++]=r;
817
818 // reset so we pass over
819 for( i=r.y0; i<r.y1; i++ )
820 memset(visitmask+i*s->xd+r.x0,0,r.x1-r.x0);
821 }
822 free(visitmask);
823
824 while(numr) {
825 int j=0;
826 // find the left most button on the top row
827 if( !s->autoorder ) {
828 for( i=1; i<numr; i++ )
829 if( rs[i].y0 < rs[j].y0 ||
830 (rs[i].y0==rs[j].y0 && rs[i].x0 < rs[j].x0 ) )
831 j=i;
832 } else {
833 for( i=1; i<numr; i++ )
834 if( rs[i].x0 < rs[j].x0 ||
835 (rs[i].x0==rs[j].x0 && rs[i].y0 < rs[j].y0 ) )
836 j=i;
837 }
838 // see if there are any buttons to the left, i.e. slightly overlapping vertically, but possibly start a little lower
839 for( i=0; i<numr; i++ )
840 if( i!=j ) {
841 int a,d;
842 if(buttonrelpos(rs+i,rs+j,&a,&d))
843 if( a==(s->autoorder?0:270) )
844 j=i;
845 }
846
847 // ok add rectangle 'j'
848
849 for( i=0; i<s->numbuttons; i++ )
850 if( s->buttons[i].r.x0<0 )
851 break;
852 if( i==s->numbuttons ) {
853 s->numbuttons++;
854 s->buttons=realloc(s->buttons,s->numbuttons*sizeof(button));
855 memset(s->buttons+i,0,sizeof(button));
856 }
857
858 fprintf(stderrstderr,"INFO: Autodetect %d = %dx%d-%dx%d\n",i,rs[j].x0,rs[j].y0,rs[j].x1,rs[j].y1);
859
860 s->buttons[i].r=rs[j];
861 memmove(rs+j,rs+j+1,(numr-j-1)*sizeof(rectangle));
862 numr--;
863 }
864}
865
866static bool_Bool imgfix(stinfo *s)
867 /* fills in the subpicture/button details. */
868{
869 int i, useimg, w, h, x, y, x0, y0;
870
871 w = s->img.width;
872 h = s->img.height;
873 s->xd = w; // pickbuttongroups needs these values set
874 s->yd = h;
875
876 if (s->autooutline)
877 detectbuttons(s);
878
879 fixnames(s);
880 detectdirections(s);
881
882 s->fimg = malloc(w * h);
883 memset(s->fimg, 255, w * h); /* mark all pixels as uninitialized */
884
885 // first try not to have multiple palettes for
886 useimg = 1;
887 if (s->numbuttons)
888 {
889 do
890 {
891 if (pickbuttongroups(s, 1, useimg))
892 break;
893 if (pickbuttongroups(s, 2, useimg))
894 break;
895 if (pickbuttongroups(s, 3, useimg))
896 break;
897 useimg--;
898 }
899 while (useimg >= 0);
900 assert(useimg)((useimg) ? (void) (0) : __assert_fail ("useimg", "subgen-image.c"
, 900, __PRETTY_FUNCTION__))
; // at this point I don't want to deal with blocking the primary subtitle image
901 if (useimg < 0)
902 {
903 fprintf(stderrstderr, "ERR: Cannot pick button masks\n");
904 return false0;
905 } /*if*/
906
907 for (i = 0; i < s->numbuttons; i++)
908 {
909 /* fill in button areas of fimg */
910 button *b = &s->buttons[i];
911
912 for (y = b->r.y0; y < b->r.y1; y++)
913 for (x = b->r.x0; x < b->r.x1; x++)
914 {
915 int dc = -1, p = y * w + x, j;
916 int c = gettricolor(s, p, useimg);
917 for (j = 0; j < 4; j++)
918 if (s->groupmap[b->grp - 1][j] == c)
919 {
920 dc = j;
921 break;
922 } /*if; for*/
923 if (dc == -1)
924 { /* shouldn't occur */
925 fprintf(stderrstderr, "ERR: Button %d cannot find color %06x in group %d\n",
926 i, c, b->grp - 1);
927 assert(dc != -1)((dc != -1) ? (void) (0) : __assert_fail ("dc != -1", "subgen-image.c"
, 927, __PRETTY_FUNCTION__))
; /* instant assertion failure */
928 } /*if*/
929 if (s->fimg[p] != dc && s->fimg[p] != 255)
930 { /* pixel already occupied by another button */
931 fprintf(stderrstderr, "ERR: Overlapping buttons\n");
932 return false0;
933 } /*if*/
934 s->fimg[p] = dc;
935 } /*for; for*/
936 } /*for*/
937 } /*if s->numbuttons*/
938 for (i = 0; i < 4; i++)
939 { /* initially mark all s->pal entries as "unused" (transparent) */
940 s->pal[i].r = 255;
941 s->pal[i].g = 255;
942 s->pal[i].b = 255;
943 s->pal[i].a = 0;
944 } /*for*/
945 for (i = 0; i < w * h; i++)
946 if (s->fimg[i] != 255)
947 s->pal[s->fimg[i]] = s->img.pal[s->img.img[i]];
948 for (i = 0; i < w * h; i++)
949 /* fill in rest of fimg with "normal" image (s->img) */
950 if (s->fimg[i] == 255)
951 { /* haven't already done this pixel */
952 int j;
953 const colorspec * const p = &s->img.pal[s->img.img[i]];
954 for (j = 0; j < 4; j++)
955 /* see if colour is already in s->pal */
956 if (!memcmp(&s->pal[j], p, sizeof(colorspec)))
957 goto if_found;
958 for (j = 0; j < 4; j++) /* insert new colour in place of unused/transparent entry */
959 if (s->pal[j].a == 0 && s->pal[j].r == 255) /* not checking all the components? */
960 {
961 s->pal[j] = *p;
962 goto if_found;
963 } /*if*/
964 /* no room in s->pal */
965 fprintf(stderrstderr, "ERR: Too many colors in base picture\n");
966 return false0;
967if_found:
968 s->fimg[i] = j;
969 } /*if; for*/
970
971 // determine minimal visual area, and crop the subtitle accordingly
972 x0 = w;
973 y0 = -1;
974 s->xd = 0;
975 s->yd = 0;
976 for (i = 0, y = 0; y < h; y++)
977 {
978 for (x = 0; x < w; i++, x++)
979 {
980 if
981 (
982 s->img.pal[s->img.img[i]].a
983 ||
984 s->hlt.pal[s->hlt.img[i]].a
985 ||
986 s->sel.pal[s->sel.img[i]].a
987 )
988 {
989 if (y0 == -1)
990 y0 = y;
991 s->yd = y;
992 if (x < x0)
993 x0 = x;
994 if (x > s->xd)
995 s->xd = x;
996 } /*if*/
997 } /*for*/
998 } /*for*/
999 if (y0 == -1)
1000 { // empty image?
1001 s->xd = w;
1002 s->yd = h;
1003 return true1;
1004 } /*if*/
1005 x0 &= -2;
1006 y0 &= -2;
1007 s->xd = (s->xd + 2 - x0) & (-2);
1008 s->yd = (s->yd + 2 - y0) & (-2);
1009 for (i = 0; i < s->yd; i++)
1010 memmove(s->fimg + i * s->xd, s->fimg + i * w + x0 + y0 * w, s->xd);
1011 for (i = 0; i < s->numbuttons; i++)
1012 {
1013 button *b = &s->buttons[i];
1014 b->r.x0 += s->x0;
1015 b->r.y0 += s->y0;
1016 b->r.x1 += s->x0;
1017 b->r.y1 += s->y0;
1018 } /*for*/
1019 s->x0 += x0;
1020 s->y0 += y0;
1021 return true1;
1022 } /*imgfix*/
1023
1024bool_Bool process_subtitle(stinfo *s)
1025 /* loads the specified image files and builds the subpicture in memory. */
1026{
1027 int w=0,h=0;
1028 int iline=0;
1029
1030 if( !s ) return false0;
1031 if( read_pic(s,&s->img) ) {
1032 if(debug > -1)
1033 fprintf(stderrstderr, "WARN: Bad image, skipping line %d\n", iline - 1);
1034 return false0;
1035 }
1036 if( s->img.img && !w ) {
1037 w=s->img.width;
1038 h=s->img.height;
1039 }
1040 if( read_pic(s,&s->hlt) ) {
1041 if(debug > -1)
1042 fprintf(stderrstderr, "WARN: Bad image, skipping line %d\n", iline - 1);
1043 return false0;
1044 }
1045 if( s->hlt.img && !w ) {
1046 w=s->hlt.width;
1047 h=s->hlt.height;
1048 }
1049 if( read_pic(s,&s->sel) ) {
1050 if(debug > -1)
1051 fprintf(stderrstderr, "WARN: Bad image, skipping line %d\n", iline - 1);
1052 return false0;
1053 }
1054 if( s->sel.img && !w ) {
1055 w=s->sel.width;
1056 h=s->sel.height;
1057 }
1058
1059 if( !w ) {
1060 fprintf(stderrstderr,"WARN: No picture, skipping line %d\n",iline-1);
1061 return false0;
1062 }
1063 /* check consistent dimensions, fill in missing images with blanks */
1064 if( !s->img.img ) {
1065 constructblankpic(&s->img,w,h);
1066 fprintf(stderrstderr,"INFO: Constructing blank img\n");
1067 } else if( s->img.width!=w || s->img.height!=h ) {
1068 fprintf(stderrstderr,"WARN: Inconsistent picture widths, skipping line %d\n",iline-1);
1069 return false0;
1070 }
1071 if( !s->hlt.img ) {
1072 constructblankpic(&s->hlt,w,h);
1073 fprintf(stderrstderr,"INFO: Constructing blank hlt\n");
1074 } else if( s->hlt.width!=w || s->hlt.height!=h ) {
1075 fprintf(stderrstderr,"WARN: Inconsistent picture widths, skipping line %d\n",iline-1);
1076 return false0;
1077 }
1078 if( !s->sel.img ) {
1079 constructblankpic(&s->sel,w,h);
1080 fprintf(stderrstderr,"INFO: Constructing blank sel\n");
1081 } else if( s->sel.width!=w || s->sel.height!=h ) {
1082 fprintf(stderrstderr,"WARN: Inconsistent picture widths, skipping line %d\n",iline-1);
1083 return false0;
1084 }
1085
1086 if( !imgfix(s) ) /* data in img to fimg */
1087 {
1088 if (debug > -1)
1089 {
1090 fprintf(stderrstderr, "ERR: Blank image, skipping line %d\n", iline - 1);
1091 }
1092 return false0;
1093 }
1094
1095 return true1;
1096}
1097
1098void image_init()
1099{
1100#if defined(HAVE_MAGICK) || defined(HAVE_GMAGICK1)
1101 InitializeMagick(NULL((void*)0));
1102#endif
1103}
1104
1105void image_shutdown()
1106{
1107#if defined(HAVE_MAGICK) || defined(HAVE_GMAGICK1)
1108 DestroyMagick();
1109#endif
1110}