more build tweaks for bsd
[goodguy/history.git] / cinelerra-5.1 / libzmpeg3 / video / cc.C
1 #include "../libzmpeg3.h"
2
3 zcc_t::
4 cc_t(video_t *video)
5  : svc(this)
6 {
7   this->video = video;
8   reset();
9 }
10
11 zcc_t::
12 ~cc_t()
13 {
14 }
15
16 void zcc_t::
17 reset()
18 {
19 //zmsg(" cc\n");
20   frs_history = 0;
21   text_cb = 0;
22   font_size = fsz_normal;
23   video->reset_subtitles();
24   int sid = video->subtitle_track+1;
25   if( sid <= 0 || sid > MX_SVC ) sid = 1;
26   svc.reset(sid, this);
27 }
28
29 void zcc_t::
30 get_atsc_data(zbits_t *s)
31 {
32   /* xfer data to data[0], first byte = total size */
33   /*   <dat[1]=isz,dat[2..isz+1]=input, ...,0> */
34   int b = s->get_bits(8) & 0x1f;
35   int len = b & 0x1f;
36   uint8_t *dat = &data[0][0];
37   dat[0] = 0;
38   uint8_t *bp = dat+1;
39   uint8_t *cp = bp+1;
40 //zmsgs(" len=%d\n",len);
41   s->get_bits(8); /* rsvd */
42
43   for( int i=0; i<len; ++i ) {
44     int typ = s->get_bits(8);
45     if( (typ & 0xf8) != 0xf8 ) return;
46     int valid = typ & 0x04;
47     int ch1 = s->get_bits(8);
48     int ch2 = s->get_bits(8);
49 //zmsgs("*** typ %02x  %02x %02x\n",typ,ch1,ch2);
50     int isz = cp-bp - 1;
51     switch( typ &= 0x03 ) {
52     case 0x02: /* dvb_data */
53       if( isz == 0 ) continue; /* missed start */
54       break;
55     case 0x03: /* dvb_start */
56       if( isz > 0 ) { *bp = isz;  bp = cp++; }
57       break;
58     case 0x00: /* ntsc_f1 */
59     case 0x01: /* ntsc_f2 */
60       continue;
61     }
62     if( !valid ) continue;
63     if( cp-dat+2 > 128 ) {
64       zerr("closed caption data ovfl\n");
65       return;
66     }
67     *cp++ = ch1;
68     *cp++ = ch2;
69   }
70   /* terminate last recd */
71   if( (*bp = cp-bp - 1) > 0 ) *cp++ = 0;
72   /* dat[0] = total size */
73   dat[0] = cp - dat;
74 //zmsgs("***** size %d\n",dat[0]);
75   if( s->get_bits(8) == 0xff ) decode();
76 }
77
78 void zcc_t::
79 decode()
80 {
81   int isz = *data[0];
82 //zmsgs("seq %d, isz=%d, pict_type %c, pict_struct %3.3s %d\n",
83 //  data[0][1]>>6,isz," IPBD"[video->pict_type],
84 //  &"    TOP BTM FRM "[4*video->pict_struct],video->framenum);
85   switch( video->pict_type ) {
86   case pic_type_I:
87   case pic_type_P:
88     switch( video->pict_struct ) {
89     case pics_TOP_FIELD:
90       if( (frs_history & (frs_tf1 | frs_frm)) ) {
91         decode(1);
92         frs_history &= ~(frs_tf1 | frs_frm);
93       }
94       memcpy(&data[1],&data[0],isz);
95       frs_history |= frs_tf1;
96       break;
97     case pics_BOTTOM_FIELD:
98       if( (frs_history & frs_frm) ) {
99         decode(1);
100         frs_history &= ~frs_frm;
101       }
102       else if( (frs_history & frs_bf2) ) {
103         decode(2);
104         frs_history &= ~frs_bf2;
105       }
106       memcpy(&data[2],&data[1],isz);
107       frs_history |= frs_bf2;
108       break;
109     case pics_FRAME_PICTURE:
110       if( (frs_history & (frs_tf1 | frs_frm)) ) {
111         decode(1);
112         frs_history &= ~(frs_tf1 | frs_frm);
113       }
114       if( frs_history & frs_bf2 ) {
115         decode(2);
116         frs_history &= ~frs_bf2;
117       }
118       memcpy(&data[1],&data[0],isz);
119       frs_history = frs_frm;
120       break;
121     }
122     break;
123   case pic_type_B:
124     if( frs_history == frs_tf1 || frs_history == frs_bf2 )
125       frs_history = 0;
126     decode(0);
127     break;
128   }
129   frs_history |= frs_prc;
130   *data[0] = 0;
131 }
132
133 void zcc_t::
134 reorder()
135 {
136   if( frs_history ) {
137     if( !(frs_history & frs_prc) )
138       decode();
139     frs_history &= ~frs_prc;
140   }
141 }
142
143 void zcc_t::
144 decode(int k)
145 {
146   int isz;
147   uint8_t *dat = &data[k][1];
148   while( (isz = *dat++) > 0 ) {
149     uint8_t *bp = dat;
150     uint8_t b = *bp;
151     int sz = 2 * (b & 0x03f);
152 #if 0
153     int seq = b >> 6;
154     zmsgs("cc decode seq %d size %d/%d -",seq,isz,sz);
155     int n = isz;
156     for( int i=0; i<n; ++i ) printf(" %02x",dat[i]);
157     printf("  ");
158     for( int i=0; i<n; ++i ) printf(" %c",dat[i]>=0x20&&dat[i]<0x7f?dat[i]:'.');
159     printf("\n");
160 #endif
161     if( sz > isz ) return;
162     uint8_t *ep = sz + bp++;
163     while( bp < ep ) {
164       b = *bp++;
165       if( !b ) continue;
166       int svc_nr = b >> 5, blksz = b & 0x1f;
167       if( svc_nr == 0x07 ) {
168         if( bp >= ep ) break;
169         svc_nr = *bp++;
170         if( svc_nr < 7 || svc_nr > 63 ) break;
171       }
172       if( svc_nr == svc.id() && bp+blksz <= ep )
173         svc.append(bp,blksz);
174       bp += blksz;
175     }
176     dat += isz;
177   }
178   svc.decode();
179 }
180
181 zsvc_t::
182 svc_t(zcc_t *cc)
183 {
184   size = 0;
185   bfrs = 0;
186   sid = 1;
187   this->cc = cc;
188 };
189
190 zsvc_t::
191 ~svc_t()
192 {
193   reset(sid, cc);
194 }
195
196 void zsvc_t::
197 reset(int sid, zcc_t *cc)
198 {
199 //zmsgs(" svc %d\n",sid);
200   size = 0;
201   this->sid = sid;
202   int ctrk = sid-1;
203   if( ctrk < 0 ) ctrk = MX_SVC-1;
204   this->ctrk = ctrk;
205   this->cc = cc;
206   for( int sid=0; sid<MX_WIN; ++sid )
207     win[sid].reset(sid, this);
208   for( chr_t *bfr; (bfr=get_bfr()); ) delete [] bfr;
209 //  win[0].default_window();
210 //  curw = &win[0];
211   curw = 0;
212 }
213
214 zchr_t *zsvc_t::
215 get_bfr()
216 {
217   chr_t *bfr = bfrs;
218   if( bfr ) bfrs = *(chr_t **)bfr;
219   return bfr;
220 }
221
222 void zsvc_t::
223 bfr_put(chr_t *&bfr)
224 {
225   if( !bfr ) return;
226   *(chr_t **)bfr = bfrs;
227   bfrs = bfr;
228   bfr = 0;
229 }
230
231 void zsvc_t::
232 append(uint8_t *bp, int len)
233 {
234   int n = size+len;
235   if( n > isizeof(data) ) n = isizeof(data);
236   for( int i=size; i<n; data[i++]=*bp++);
237   size = n;
238 }
239
240 void zsvc_t::
241 decode()
242 {
243   if( size == 0 ) return;
244   bp = data;  ep = bp+size;  size = 0;
245 //zmsgs(" size=%d,sid=%d\n",(int)(ep-bp),sid);
246   while( bp < ep ) {
247     int b = *bp++;
248     if( (b&0x7f) >= 0x20 ) { // group G0/G1 ascii/latin chars
249       if( curw && curw->st > st_unmap ) curw->store(b);
250       continue;
251     }
252     if( b < 0x10 || b >= 0x80 ) { // group C0/C1
253       if( command(b) ) break;
254       continue;
255     }
256     if( b == 0x10 ) {  // ETX1 escape C2/C3, G2/G3
257       if( bp >= ep ) break;
258       b = *bp++;       // no valid codes, just skip rsvd bytes
259       if( b < 0x08 ) continue;                 // 0x00-0x07 C2
260       if( b < 0x10 ) { bp += 1; continue; }    // 0x08-0x0f C2
261       if( b < 0x18 ) { bp += 2; continue; }    // 0x10-0x17 C2
262       if( b < 0x20 ) { bp += 3;  continue; }   // 0x18-0x1f C2
263       if( b < 0x80 ) continue;  /* misc */     // 0x20-0x7f G2
264       if( b < 0x88 ) { bp += 4;  continue; }   // 0x80-0x87 C3
265       if( b < 0x90 ) { bp += 5;  continue; }   // 0x88-0x8f C3
266       continue;
267     }
268     int bb = b & 0x07;          // one byte >ETX1 0x11-0x17
269     if( bp >= ep ) break;
270     bb = (bb << 8) | *bp++;
271     if( b >= 0x18 ) {           // two byte >=P16 0x18-0x20
272       if( bp >= ep ) break;
273       bb = (bb << 8) | *bp++;
274     }
275     if( curw && curw->st > st_unmap ) curw->store(bb);
276   }
277   render();
278 }
279
280 int zsvc_t::
281 render()
282 {
283   /* render dirty windows */
284   for( int iw=0; iw<MX_WIN; ++iw ) {
285     win_t *wp = &win[iw];
286     if( wp->dirty ) {
287       wp->render();
288       wp->dirty = 0;
289     }
290   }
291   return 0;
292 }
293
294 int zsvc_t::
295 command(int cmd)
296 {
297   if( cmd == 0x00 || cmd == 0x03 || // nuisance
298       cmd == 0x8d || cmd == 0x8e ) return 0;
299 #define CMD(fn,args...) return fn(args)
300 //zmsgs("cmd %02x svc %d ",cmd,sid);
301 //#define CMD(fn,args...) printf("%s\n",#fn); return fn(args)
302   switch( cmd ) {
303   case 0x03: CMD(ETX);      /* End of text, render */
304   case 0x08: CMD(BS);       /* Backspace */
305   case 0x0C: CMD(FF);       /* Form Feed */
306   case 0x0D: CMD(CR);       /* Carriage Return */
307   case 0x0E: CMD(HCR);      /* Horizontal Carriage Return */
308   case 0x80 ... 0x87:
309     CMD(CWx,cmd & 0x07);    /* SetCurrentWindow */
310   case 0x88: CMD(CLW);      /* ClearWindows */
311   case 0x89: CMD(DSW);      /* DisplayWindows */
312   case 0x8A: CMD(HDW);      /* HideWindows */
313   case 0x8B: CMD(TGW);      /* ToggleWindows */
314   case 0x8C: CMD(DLW);      /* DeleteWindows */
315   case 0x8D: CMD(DLY);      /* Delay */
316   case 0x8E: CMD(DLC);      /* Delay Cancel */
317   case 0x8F: CMD(RST);      /* Reset */
318   case 0x90: CMD(SPA);      /* SetPenAttributes */
319   case 0x91: CMD(SPC);      /* SetPenColor */
320   case 0x92: CMD(SPL);      /* SetPenLocation */
321   case 0x93 ... 0x96:
322     CMD(RSVD);              /* reserved */
323   case 0x97: CMD(SWA);      /* SetWindowAttributes */
324   case 0x98 ... 0x9F:
325     CMD(DFx,cmd & 0x07);    /* DFx DefineWindow */
326   }
327   zerrs("unkn %02x\n",cmd);
328   return 1;
329 }
330
331 static inline int RGB2Y(int r,int g,int b)
332 {
333   return ( 257*r+504*g+ 98*b+ 16500)/1000;
334 }
335
336 static inline int RGB2Cr(int r,int g,int b)
337 {
338   return ( 439*r-368*g- 71*b+128500)/1000;
339 }
340
341 static inline int RGB2Cb(int r,int g,int b)
342 {
343   return (-148*r-291*g+439*b+128500)/1000;
344 }
345
346 uint32_t zsvc_t::
347 pen_yuva(int color, int opacity)
348 {
349   int r, g, b, y, u, v, a;
350   r = ((color>>4) & 0x03) * 255 / 3;
351   g = ((color>>2) & 0x03) * 255 / 3;
352   b = ((color>>0) & 0x03) * 255 / 3;
353   a = 255;
354   switch( opacity ) {
355   case oty_transparent:  a =   0;  break;
356   case oty_translucent:  a = 128;  break;
357   case oty_flash:        a = 208;  break;
358   case oty_solid:        a = 255;  break;
359   }
360   y = RGB2Y(r,g,b);
361   u = RGB2Cr(r,g,b);
362   v = RGB2Cb(r,g,b);
363   return (y<<24) | (u<<16) | (v<<8) | (a<<0);
364 }
365
366 static inline int put_utf8(unsigned int v, uint8_t *cp, int n)
367 {
368   if( v >= 0x80 ) {
369     int l = v < 0x0800 ? 2 : v < 0x00010000 ? 3 :
370       v < 0x00200000 ? 4 : v < 0x04000000 ? 5 : 6;
371     if( l > n ) return -1;
372     int m = 0xff00 >> l, i = l;
373     *cp++ = (v>>(6*--i)) | m;
374     for( ; --i>=0; ++cp ) *cp = ((v>>(6*i)) & 0x3f) | 0x80;
375     return l;
376   }
377   if( n < 1 ) return -1;
378   *cp = v;
379   return 1;
380 }
381
382 zwin_t::
383 win_t()
384 {
385   bfr = 0;
386   dirty = 0;
387   subtitle = 0;
388 }
389
390 zwin_t::
391 ~win_t()
392 {
393   delete [] bfr;
394   delete subtitle;
395 }
396
397 void zwin_t::
398 output_text()
399 {
400   if( st < st_visible ) return;
401   end_frame = svc->cc->video->framenum;
402   if( !svc->cc->text_cb ) return;
403   if( !bfr ) return;
404   char txt[1024];
405   uint8_t *cp = (uint8_t *)txt, *ep = cp + sizeof(txt)-1;
406   for( int iy=0; iy<ch; ++iy ) {
407     if( iy && cp < ep ) *cp++ = '|';
408     chr_t *bp = &bfr[iy * MX_CX];
409     for( int ix=0; ix<cw && cp<ep; ++ix,++bp ) {
410       if( !bp->glyph ) continue;
411       int n = put_utf8(bp->glyph, cp, ep-cp);
412       if( n < 0 ) break;
413       cp += n;
414     }
415   }
416   *cp = 0;
417   svc->cc->text_cb(svc->sid, id, start_frame, end_frame, txt);
418   start_frame = end_frame;
419 }
420
421 void zwin_t::
422 set_state(int8_t st)
423 {
424   if( this->st == st ) return;
425 //zmsgs("win %d was %d, now %d %s\n", id, this->st, st,
426 //  st==st_unmap ? "unmapped" : st==st_hidden ? "hidden" :
427 //  st==st_visible ? "visible" : "err");
428   output_text();
429   if( this->st < st_visible && st >= st_visible )
430     start_frame = svc->cc->video->framenum;
431   this->st = st;
432   dirty = 1;
433 }
434
435 void zwin_t::
436 reset(int i, zsvc_t *svc)
437 {
438   this->id = i;
439   this->svc = svc;
440 //zmsgs("win %d\n",id);
441   scale = 1;
442   cx = cy = cw = ch = 0;
443   x = y = 0;
444   pdir = dir_rt;
445   sdir = dir_up;
446   svc->bfr_put(bfr);
447   st = st_unmap;
448 }
449
450 int zwin_t::
451 is_breakable(int b)
452 {
453   return b == ' ' || b == '-' || b == '\r';
454 }
455
456 int zwin_t::
457 wrap()
458 {
459   switch( pdir ) {
460   case dir_rt: {
461     chr_t line[MX_CX];  int ix = cx-1;
462     chr_t *cp = &line[ix], *sp = cp;
463     while( ix >= 0 ) {
464       chr_t *bp = &bfr[cy*MX_CX + ix];
465       if( is_breakable(bp->glyph) ) break;
466       *cp-- = *bp;  bp->glyph = 0;  --ix;
467     }
468     if( ix >= 0 ) CR();
469     for( ; cx<cw && cp<sp; ++cx )
470       bfr[cy*MX_CX + cx] = *++cp;
471     if( ix < 0 ) CR();
472     break; }
473
474   case dir_lt: {
475     chr_t line[MX_CX];  int ix = cx+1;
476     chr_t *cp = &line[ix], *sp = cp;
477     while( ix < cw ) {
478       chr_t *bp = &bfr[cy*MX_CX + ix];
479       if( is_breakable(bp->glyph) ) break;
480       *cp++ = *bp;  bp->glyph = 0;  ++ix;
481     }
482     if( ix < cw ) CR();
483     for( ; cx>=0 && cp>sp; --cx )
484       bfr[cy*MX_CX + cx] = *--cp;
485     if( ix >= cw ) CR();
486     break; }
487
488   case dir_dn: {
489     chr_t line[MX_CY];  int iy = cy-1;
490     chr_t *cp = &line[iy], *sp = cp;
491     while( iy >= 0 ) {
492       chr_t *bp = &bfr[iy*MX_CX + cx];
493       if( is_breakable(bp->glyph) ) break;
494       *cp-- = *bp;  bp->glyph = 0;  --iy;
495     }
496     if( iy >= 0 ) CR();
497     for( ; cy<ch && cp<sp; ++cy )
498       bfr[cy*MX_CX + cx] = *++cp;
499     if( iy < 0 ) CR();
500     break; }
501
502   case dir_up: {
503     chr_t line[MX_CY];  int iy = cy+1;
504     chr_t *cp = &line[iy], *sp = cp;
505     while( iy < ch ) {
506       chr_t *bp = &bfr[iy*MX_CX + cx];
507       if( is_breakable(bp->glyph) ) break;
508       *cp++ = *bp;  bp->glyph = 0;  ++iy;
509     }
510     if( iy < ch ) CR();
511     for( ; cy>=0 && cp>sp; --cy )
512       bfr[cy*MX_CX + cx] = *--cp;
513     if( iy >= ch ) CR();
514     break; }
515   }
516
517   return 0;
518 }
519
520 int zwin_t::
521 ovfl()
522 {
523   if( !col_lock && !resize(cx+1, ch) ) return 0;
524   if( !row_lock ) return 1;
525   if( wordwrap ) return wrap();
526   return CR();
527 }
528
529 int zwin_t::
530 store(int b)
531 {
532 //zmsgs("%02x ** \"%c\" at %d,%d win %d\n",
533 //  b, b>=0x20 && b<0x7f ? b : '.',cx,cy,id);
534   if( !bfr ) return 1;
535   switch( pdir ) {
536   case dir_rt:  if( cx >= cw && ovfl() ) return 1;  break;
537   case dir_lt:  if( cx < 0 && ovfl() )   return 1;  break;
538   case dir_dn:  if( cy >= ch && ovfl() ) return 1;  break;
539   case dir_up:  if( cy < 0 && ovfl() )   return 1;  break;
540   }
541   if( cx >= 0 && cx < cw && cy >= 0 && cy < ch ) {
542     chr_t *chr = &bfr[cy*MX_CX + cx];
543     chr->glyph = b;
544     chr->fg_opacity = fg_opacity;
545     chr->fg_color = fg_color;
546     chr->bg_opacity = bg_opacity;
547     chr->bg_color = bg_color;
548     chr->pen_size = pen_size;
549     chr->edge_color = edge_color;
550     chr->italics = italics;
551     chr->edge_type = edge_type;
552     chr->underline = underline;
553     chr->font_style = font_style;
554     chr->font_size = svc->cc->font_size;
555     chr->offset = offset;
556     chr->text_tag = text_tag;
557     dirty = 1;
558   }
559   switch( pdir ) {
560   case dir_rt:  ++cx;  break;
561   case dir_lt:  --cx;  break;
562   case dir_dn:  ++cy;  break;
563   case dir_up:  --cy;  break;
564   }
565   return 0;
566 }
567
568 int zsvc_t::
569 ETX() /* end of text */
570 {
571 //zmsgs("svc %d,win %d\n",sid, curw ? curw->id : -1);
572 //  if( curw ) curw->render();
573   return 0;
574 }
575
576 int zwin_t::
577 BS() /* Backspace */
578 {
579   switch( pdir ) {
580   case dir_rt:  if( cx > 0 ) --cx;   break;
581   case dir_lt:  if( cx < cw-1 ) ++cx;  break;
582   case dir_dn:  if( cy > 0 ) --cy;   break;
583   case dir_up:  if( cy < ch-1 ) ++cy;  break;
584   }
585   return 0;
586 }
587
588 int zsvc_t::
589 BS() /* Backspace */
590 {
591   if( !curw || curw->st == st_unmap ) return 0;
592 //zmsgs("svc %d,win %d\n",sid,curw->id);
593   return curw->BS();
594 }
595
596 int zwin_t::
597 FF() /* Form Feed */
598 {
599   int n = cw*sizeof(*bfr);
600   for( int iy=0; iy<ch; ++iy )
601     memset(&bfr[iy*MX_CX],0,n);
602   cx = cy = 0;
603   dirty = 1;
604   return 0;
605 }
606
607 int zsvc_t::
608 FF() /* Form Feed */
609 {
610   if( !curw || curw->st == st_unmap ) return 0;
611 //zmsgs("svc %d,win %d\n",sid,curw->id);
612   return curw->FF();
613 }
614
615 int zwin_t::
616 CR() /* Carriage Return */
617 {
618   int n = (cw-1)*sizeof(*bfr);
619   int m = cw*sizeof(*bfr);
620   int scrolled = 0;
621   switch( sdir ) {
622   case dir_rt:
623     cy = 0;
624     if( cx > 0 ) { --cx;  break; }
625     for( int i=0; i<ch; ++i ) {
626       memmove(&bfr[i*MX_CX+1],&bfr[i*MX_CX],n);
627       memset(&bfr[i*MX_CX],0,sizeof(*bfr));
628     }
629     cx = 0;
630     scrolled = 1;
631     break;
632   case dir_lt:
633     cy = 0;
634     if( cx+1 < cw ) { ++cx;  break; }
635     for( int i=0; i<ch; ++i ) {
636       memmove(&bfr[i*MX_CX],&bfr[i*MX_CX+1],n);
637       memset(&bfr[i*MX_CX+cw-1],0,sizeof(*bfr));
638     }
639     cx = cw-1;
640     scrolled = 1;
641     break;
642   case dir_dn:
643     cx = 0;
644     if( cy > 0 ) { --cy;  break; }
645     for( int i=ch; --i>0; )
646       memmove(&bfr[i*MX_CX],&bfr[(i-1)*MX_CX],m);
647     memset(&bfr[0],0,m);
648     cy = 0;
649     scrolled = 1;
650     break;
651   case dir_up:
652     cx = 0;
653     if( cy+1 < ch ) { ++cy;  break; }
654     for( int i=1; i<ch; ++i )
655       memmove(&bfr[(i-1)*MX_CX],&bfr[i*MX_CX],m);
656     memset(&bfr[(ch-1)*MX_CX],0,m);
657     cy = ch-1;
658     scrolled = 1;
659     break;
660   }
661   if( scrolled ) {
662     output_text();
663     dirty = 1;
664   }
665   return 0;
666 }
667
668 int zsvc_t::
669 CR() /* Carriage Return */
670 {
671   if( !curw || curw->st == st_unmap ) return 0;
672 //zmsgs("svc %d,win %d\n",sid,curw->id);
673   return curw->CR();
674 }
675
676 int zwin_t::
677 HCR() /* Horizontal Carriage Return */
678 {
679   switch( pdir ) {
680   case dir_rt:
681   case dir_lt:
682     memset(&bfr[cy*MX_CX],0,cw*sizeof(*bfr));
683     cy = pdir == dir_rt ? 0 : cw-1;
684     dirty = 1;
685     break;
686   case dir_up:
687   case dir_dn:
688     for( int i=0; i<ch; ++i ) memset(&bfr[i*MX_CX+cx],0,sizeof(*bfr));
689     cx = pdir == dir_dn ? 0 : ch-1;
690     dirty = 1;
691     break;
692   }
693   return 0;
694 }
695
696 int zsvc_t::
697 HCR() /* Horizontal Carriage Return */
698 {
699   if( !curw || curw->st == st_unmap ) return 0;
700 //zmsgs("svc %d,win %d\n",sid,curw->id);
701   return curw->HCR();
702 }
703
704 int zsvc_t::
705 CLW()  /* ClearWindows */
706 {
707   if( bp >= ep ) return 1;
708   int map = *bp++;  // 1
709 //zmsgs("map %02x svc %d\n",map,sid);
710   for( int iw=0; iw<MX_WIN; ++iw ) {
711     if( !(map & (1<<iw)) ) continue;
712     win_t *wp = &win[iw];
713     if( wp->st > st_unmap ) {
714       wp->output_text();
715 //zmsgs("  clear win %d\n",wp->id);
716       int n = wp->cw*sizeof(*wp->bfr);
717       for( int iy=0; iy<wp->ch; ++iy )
718         memset(&wp->bfr[iy*MX_CX],0,n);
719       wp->dirty = 1;
720     }
721   }
722   return 0;
723 }
724
725 int zsvc_t::
726 DSW() /* DisplayWindows */
727 {
728   if( bp >= ep ) return 1;
729   int map = *bp++;  // 1
730 //zmsgs("map %02x svc %d\n",map,sid);
731   for( int iw=0; iw<MX_WIN; ++iw ) {
732     if( !(map & (1<<iw)) ) continue;
733     win_t *wp = &win[iw];
734     if( wp->st > st_unmap ) {
735       wp->set_state(st_visible);
736     }
737   }
738   return 0;
739 }
740
741 int zsvc_t::
742 HDW() /* HideWindows */
743 {
744   if( bp >= ep ) return 1;
745   int map = *bp++;  // 1
746 //zmsgs("map %02x svc %d\n",map,sid);
747   for( int iw=0; iw<MX_WIN; ++iw ) {
748     if( !(map & (1<<iw)) ) continue;
749     win_t *wp = &win[iw];
750     if( wp->st > st_unmap ) {
751       wp->set_state(st_hidden);
752     }
753   }
754   return 0;
755 }
756
757 int zsvc_t::
758 TGW() /* ToggleWindows */
759 {
760   if( bp >= ep ) return 1;
761   int map = *bp++;  // 1
762 //zmsgs("map %02x svc %d\n",map,sid);
763   for( int iw=0; iw<MX_WIN; ++iw ) {
764     if( !(map & (1<<iw)) ) continue;
765     win_t *wp = &win[iw];
766     if( wp->st > st_unmap ) {
767       int8_t st = wp->st == st_visible ? st_hidden : st_visible;
768       wp->set_state(st);
769     }
770   }
771   return 0;
772 }
773
774 int zsvc_t::
775 DLW() /* DeleteWindows */
776 {
777   if( bp >= ep ) return 1;
778   int map = *bp++;  // 1
779 //zmsgs("map %02x svc %d\n",map,sid);
780   for( int iw=0; iw<MX_WIN; ++iw ) {
781     if( !(map & (1<<iw)) ) continue;
782     win_t *wp = &win[iw];
783     if( wp->st == st_unmap ) continue;
784 //zmsgs("  delete %d\n",wp->id);
785     wp->set_state(st_unmap);
786     if( wp == curw ) curw = 0;
787     bfr_put(wp->bfr);
788   }
789   return 0;
790 }
791
792 int zsvc_t::
793 DLY() /* delay */
794 {
795   if( bp >= ep ) return 1;
796 //zmsgs(" svc %d (%d)\n",sid, *bp);
797   ++bp;  // 1
798   return 0;
799 }
800
801 int zsvc_t::
802 DLC() /* delay cancel */
803 {
804 //zmsg("\n");
805   return 0;
806 }
807
808 int zsvc_t::
809 RST() /* Reset */
810 {
811 //zmsgs("svc %d\n",sid);
812   reset(sid, cc);
813   return 0;
814 }
815
816 int zsvc_t::
817 SPA() /* SetPenAttributes */
818 {
819   if( bp+2 > ep ) return 1;
820   int b = *bp++;  // 1
821   int text_tag = b>>4;
822   int offset = (b>>2) & 0x03;
823   int pen_size = b & 0x03;
824   b = *bp++;  // 2
825   int italics = b>>7;
826   int underline = (b>>6) & 0x01;
827   int edge_type = (b>>3) & 0x07;
828   int font_style = b & 0x07;
829   if( pen_size >= 3 ) return 0;
830   if( offset >= 3 ) return 0;
831   if( edge_type >= 6 ) return 0;
832   win_t *wp = curw;
833   if( wp && wp->st > st_unmap ) {
834     wp->text_tag = text_tag;
835     wp->offset = offset;
836     wp->pen_size = pen_size;
837     wp->italics = italics;
838     wp->underline = underline;
839     wp->edge_type = edge_type;
840     wp->font_style = font_style;
841 //zmsgs("txttag=%d,offset=%d,pen_size=%d,ital=%d,undl=%d,"
842 //  "edge=%d,ftstyl=%d,win %d,svc %d\n",text_tag,offset,
843 //  pen_size,italics,underline,edge_type,font_style,wp->id,sid);
844   }
845   return 0;
846 }
847
848 int zsvc_t::
849 SPC() /* SetPenColor */
850 {
851   if( bp+3 > ep ) return 1;
852   int b = *bp++;  // 1
853   int fg_opacity = b >> 6;
854   int fg_color = b & 0x3f;
855   b = *bp++;  // 2
856   int bg_opacity = b >> 6;
857   int bg_color = b & 0x3f;
858   b = *bp++;  // 3
859   int edge_color = b;
860   win_t *wp = curw;
861   if( wp && wp->st > st_unmap ) {
862     wp->fg_opacity = fg_opacity;
863     wp->fg_color = fg_color;
864     wp->edge_color = edge_color;
865     wp->bg_opacity = bg_opacity;
866     wp->bg_color = bg_color;
867     wp->fg_yuva = pen_yuva(fg_color,fg_opacity);
868     wp->edge_yuva = pen_yuva(edge_color,fg_opacity);
869     wp->bg_yuva = pen_yuva(bg_color,bg_opacity);
870 //zmsgs("fgopc=%d,fgclr=%02x,bgopc=%d,bgclr=%02x,egclr=%02x,"
871 //  "win %d,svc %d\n",fg_opacity,fg_color,bg_opacity,bg_color,
872 //  edge_color,wp->id,sid);
873   }
874   return 0;
875 }
876
877 int zsvc_t::
878 SPL() /* SetPenLocation */
879 {
880   if( bp+2 > ep ) return 1;
881   int b = *bp++;  // 1
882   int row = b;
883   b = *bp++;  // 2
884   int column = b;
885   if( row >= MX_CY ) return 0;
886   if( column >= MX_CX ) return 0;
887   win_t *wp = curw;
888   if( wp && wp->st > st_unmap ) {
889     if( column >= wp->cw || row >= wp->ch ) return 1;
890     wp->cx = column;  wp->cy = row;
891 //zmsgs("row=%d,col=%d,win %d,svc %d\n",row,column,wp->id,sid);
892   }
893   return 0;
894 }
895
896 int zsvc_t::
897 RSVD() /* reserved */
898 {
899 //zmsgs("sid %d\n",sid);
900   return 0;
901 }
902
903 int zsvc_t::
904 SWA() /* SetWindowAttributes */
905 {
906   if( bp+4 > ep ) return 1;
907   int b = *bp++;  // 1
908   int fill_opacity = b>>6;
909   int fill_color = b & 0x3f;
910   b = *bp++;  // 2
911   int border_color = b & 0x3f;
912   int border_type = b>>6;
913   b = *bp++;  // 3
914   int wordwrap = (b>>6) & 0x01;
915   border_type |= (b>>5) & 0x04;
916   int print_direction = (b>>4) & 0x03;
917   int scroll_direction = (b>>2) & 0x03;
918   int justify = b & 0x03;
919   b = *bp++;  // 4
920   int effect_speed = b>>4;
921   int effect_direction = (b>>2) & 0x3;
922   int display_effect = b & 0x03;
923   if( border_type >= 6 ) return 0;
924   if( display_effect >= 3 ) return 0;
925
926   win_t *wp = curw;
927   if( wp && wp->st > st_unmap ) {
928     wp->fill_opacity = fill_opacity;
929     wp->fill_color = fill_color;
930     wp->border_color = border_color;
931     wp->fill_yuva = pen_yuva(fill_color,fill_opacity);
932     wp->border_yuva = pen_yuva(border_color,fill_opacity);
933     wp->wordwrap = wordwrap;
934     wp->pdir = print_direction;
935     wp->sdir = scroll_direction;
936     wp->justify = justify;
937     wp->effect_speed = effect_speed;
938     wp->effect_direction = effect_direction;
939     wp->display_effect = display_effect;
940     wp->dirty = 1;
941 //zmsgs("fopc=%d,fclr=%02x,bclr=%02x,wrap=%d,pdir=%d,sdir=%d,just=%d"
942 //  "espd=%d,edir=%d,deft=%d,win %d,svc %d\n",fill_opacity,fill_color,
943 //  border_color,wordwrap,print_direction,scroll_direction,justify,
944 //  effect_speed,effect_direction,display_effect,wp->id,sid);
945   }
946   return 0;
947 }
948
949 int zsvc_t::
950 CWx(int id) /* SetCurrentWindow */
951 {
952   win_t *wp = &win[id];
953   curw = wp->st == st_unmap ? 0 : wp;
954 //zmsgs("== win %d,svc %d\n",wp->id,sid);
955   return 0;
956 }
957
958 int zsvc_t::
959 DFz(int id) /* DFx DefineWindow */
960 {
961   if( bp+6 > ep ) return 1;
962   win_t *wp = &win[id];
963   int b = *bp++; // 1
964   int visible = (b>>5) & 0x01;
965   int row_lock = (b>>4) & 0x01;
966   int col_lock = (b>>3) & 0x01;
967   int priority = b & 0x07;
968   b = *bp++; // 2
969   int anchor_relative = (b>>7) & 0x01;
970   int anchor_vertical = b & 0x7f;
971   b = *bp++; // 3
972   int anchor_horizontal = b;
973   b = *bp++; // 4
974   int anchor_point = b>>4;
975   int ch = (b & 0x0f) + 1;
976   b = *bp++; // 5
977   int cw = b + 1;
978   b = *bp++; // 6
979   int window_style = (b>>3) & 0x07;
980   int pen_style = b & 0x07;
981 //zmsgs("%dx%d vis=%d,rowlk=%d,collk=%d,pri=%d,an_rel=%d,\n"
982 //  "an_hor=%d,an_vert=%d,an_pnt=%d,wsty=%d,psty=%d,win %d,svc %d\n",
983 //  cw,ch,visible,row_lock,col_lock,priority,anchor_relative,
984 //  anchor_horizontal,anchor_vertical,anchor_point,window_style,
985 //  pen_style,wp->id,sid);
986   if( anchor_relative ) {
987     if( anchor_vertical >= 75 ) return -1;
988     if( anchor_horizontal >= 210 ) return -1;
989   }
990   else {
991     if( anchor_vertical >= 100 ) return -1;
992     if( anchor_horizontal >= 100 ) return -1;
993   }
994   if( anchor_point >= 9 ) return -1;
995   if( cw > MX_CX ) return -1;
996   if( ch > MX_CY ) return -1;
997
998   wp->row_lock = row_lock;
999   wp->col_lock = col_lock;
1000   wp->priority = priority;
1001   wp->anchor_relative = anchor_relative;
1002   wp->anchor_vertical = anchor_vertical;
1003   wp->anchor_horizontal = anchor_horizontal;
1004   wp->anchor_point = anchor_point;
1005   if( wp->st == st_unmap )
1006     wp->set_window_style(window_style);
1007   if( wp->st == st_unmap )
1008     wp->set_pen_style(pen_style);
1009   int bw = wp->border_type != edg_none ? bdr_width : 0;
1010   wp->bw = (bw+3) & ~4;  // yuv 422 macro pixels
1011   if( wp->st == st_unmap )
1012     wp->init(cw, ch);
1013   else
1014     wp->resize(cw, ch);
1015   wp->set_state(visible ? st_visible : st_hidden);
1016   curw = wp;
1017   wp->dirty = 1;
1018   int scale = cc->video->coded_picture_height/360;
1019   if( scale < 1 ) scale = 1;
1020   wp->scale = scale;
1021   return 0;
1022 }
1023
1024 int zsvc_t::
1025 DFx(int id) /* DFx DefineWindow wrapper */
1026 {
1027   int ret = DFz(id);
1028   if( ret < 0 ) {
1029     zmsgs("failed to validate cc win %d\n",id);
1030     ret = 0;
1031   }
1032   return ret;
1033 }
1034
1035 int zwin_t::
1036 init(int ncw, int nch)
1037 {
1038 //zmsgs("  init %d, %dx%d\n", id, ncw, nch);
1039   if( ncw > MX_CX ) return 1;
1040   if( nch > MX_CY ) return 1;
1041   bfr = svc->get_bfr();
1042   if( !bfr )
1043     bfr = new chr_t[MX_CX*MX_CY];
1044   cw = ncw;  ch = nch;
1045   int n = cw*sizeof(*bfr);
1046   for( int iy=0; iy<ch; ++iy )
1047     memset(&bfr[iy*MX_CX],0,n);
1048   text_tag = 0;
1049   cx = cy = 0;
1050   x = y = 0;
1051   return 0;
1052 }
1053
1054 int zwin_t::
1055 resize(int ncw, int nch)
1056 {
1057 //zmsgs("  resize %d, %dx%d %s\n", id, ncw, nch, cw!=ncw || ch!=nch ? "*" : "");
1058   if( ncw > MX_CX ) return 1;
1059   if( nch > MX_CY ) return 1;
1060   int dx = ncw-cw, dy = nch-ch;
1061   if( !dx && !dy ) return 0;
1062   int n = cw*sizeof(*bfr);
1063   switch( sdir ) {
1064   case dir_rt:
1065     if( dx > 0 ) { // scroll right, expanding
1066       int m = dx*sizeof(*bfr);
1067       for( int iy=0; iy<ch; ++iy ) {
1068         chr_t *bp = &bfr[iy*MX_CX], *cp = bp+dx;
1069         memmove(cp,bp,n);
1070         memset(bp,0,m);
1071       }
1072       dx = 0;
1073     }
1074     break;
1075   case dir_lt:
1076     if( dx < 0 ) { // scroll left, contracting
1077       for( int iy=0; iy<ch; ++iy ) {
1078         chr_t *bp = &bfr[iy*MX_CX], *cp = bp-dx;
1079         memmove(bp,cp,n);
1080       }
1081       dx = 0;
1082     }
1083     break;
1084   case dir_dn:
1085     if( dy > 0 ) { // scroll down, expanding
1086       for( int iy=ch; --iy>=dy; ) {
1087         chr_t *bp = &bfr[iy*MX_CX], *cp = bp-dy*MX_CX;
1088         memmove(bp,cp,n);
1089       }
1090       memset(&bfr[0],0,dy*n);
1091       dy = 0;
1092     }
1093     break;
1094   case dir_up:
1095     if( dy < 0 ) { // scroll up, contracting
1096       for( int iy=dy; iy>ch; ++iy ) {
1097         chr_t *bp = &bfr[iy*MX_CX], *cp = bp+dy*MX_CX;
1098         memmove(cp,bp,n);
1099       }
1100       dy = 0;
1101     }
1102     break;
1103   }
1104
1105   if( dx > 0 ) {
1106     int m = dx*sizeof(*bfr);
1107     for( int iy=0; iy<ch; ++iy ) {
1108       chr_t *bp = &bfr[iy*MX_CX] + cw;
1109       memset(bp,0,m);
1110     }
1111   }
1112   if( dy > 0 ) {
1113     n = ncw*sizeof(*bfr);
1114     for( int iy=ch; iy<nch; ++iy ) {
1115       chr_t *bp = &bfr[iy*MX_CX];
1116       memset(bp,0,n);
1117     }
1118   }
1119   cw = ncw;  ch = nch;
1120   return 0;
1121 }
1122
1123 void zwin_t::
1124 set_pen_style(int i)
1125 {
1126   pen_style = i > 0 ? i : 1;
1127   pen_size = psz_standard;
1128   font_style = pen_style <= 5 ? pen_style-1 : pen_style-3;
1129   offset = ofs_normal;
1130   italics = underline = 0;
1131   fg_opacity = oty_solid;
1132   fg_color = 0x3f;
1133   edge_type = pen_style <= 5 ? edg_none : edg_uniform;
1134   edge_color = 0x00;
1135   bg_opacity = pen_style <= 5 ? oty_solid : oty_transparent;
1136   bg_color = 0x00;
1137   fg_yuva = pen_yuva(fg_color,fg_opacity);
1138   edge_yuva = pen_yuva(edge_color,fg_opacity);
1139   bg_yuva = pen_yuva(bg_color,bg_opacity);
1140   text_tag = tag_dialog;
1141 }
1142  
1143 void zwin_t::
1144 set_window_style(int i)
1145 {
1146   window_style = i > 0 ? i : 1;
1147   justify = ((0x48>>window_style) & 1) ? jfy_center : jfy_left;
1148   pdir = window_style < 7 ? dir_rt : dir_dn;
1149   sdir = window_style < 7 ? dir_up : dir_lt;
1150   wordwrap = (0x70>>window_style) & 1;
1151   display_effect = eft_snap;
1152   effect_speed = 0;
1153   effect_direction = dir_rt;
1154   fill_opacity = oty_solid;
1155   fill_color = 0x00;
1156   border_type = edg_none;
1157   border_color = 0x00;
1158   fill_yuva = pen_yuva(fill_color,fill_opacity);
1159   border_yuva = pen_yuva(border_color,fill_opacity);
1160 }
1161
1162 #if 0
1163 void zwin_t::
1164 default_window()
1165 {
1166   if( !bfr ) return;
1167   row_lock = col_lock = 0;
1168   priority = 0;
1169   anchor_relative = 0;
1170   anchor_vertical = 24;
1171   anchor_horizontal = 12;
1172   anchor_point = 0;
1173   set_window_style(1);
1174   set_pen_style(1);
1175   text_tag = 0;
1176   ch = 1;  cw = 32;
1177   int n = cw*sizeof(*bfr);
1178   for( int iy=0; iy<ch; ++iy)
1179     memset(&bfr[iy*MX_CX],0,n);
1180   cx = cy = 0;
1181   st = st_visible;
1182 }
1183 #endif
1184
1185 void zwin_t::
1186 print_buffer()
1187 {
1188   if( !bfr ) return;
1189   zmsgs("bfr %d\n",id);
1190   for( int iy=0; iy<ch; ++iy ) {
1191     int ofs = iy*MX_CX;
1192     for( int ix=0; ix<cw; ++ix ) {
1193       int b = bfr[ofs++].glyph;
1194       printf("%c", b>=0x20 && b<0x7f ? b : '.');
1195     }
1196     printf("\n");
1197   }
1198 }
1199
1200 void zwin_t::
1201 put_chr(chr_t *chr)
1202 {
1203   if( !subtitle ) return;
1204   uint16_t bb = chr->glyph;
1205   bitfont_t *font = chr_font(chr, scale);
1206   if( bb >= font->nch ) return;
1207 //zmsgs("%02x \"%c\" at %d,%d in %d\n",bb,bb>=0x20&&bb<0x7f?bb:'.',x,y,id);
1208   int ww = subtitle->w - 2*bw;
1209   int hh = subtitle->h - 2*bw;
1210   int dx = 0, dy = 0;
1211   bitfont_t::bitchar_t *cp = &font->chrs[bb];
1212   uint8_t *bmap = cp->bitmap;
1213   if( bmap || cp->ch == ' ' ) {
1214     /* cell origin = (x,y) addressed (0,0) to (x2,y2) */
1215     /* char data box (x0,y0) to (x1,y1), may exceed cell box */
1216     int x0, ix0, y0, iy0;
1217     int x1, x2, y1, y2;
1218     x0 = ix0 = cp->left;
1219     y0 = iy0 = font->imy - cp->ascent;
1220     x1 = cp->right;
1221     y1 = font->imy + cp->decent;
1222     x2 = font->idx;  y2 = font->idy;
1223     if( bmap ) {
1224       switch( pdir ) {
1225       case dir_rt:  x2 = dx = cp->right;  break;
1226       case dir_lt:  x2 = dx = cp->left;   break;
1227       case dir_dn:  y2 = dy = cp->decent;  break;
1228       case dir_up:  y2 = dy = cp->ascent;  break;
1229       }
1230     }
1231     else { /* blank */
1232       switch( pdir ) {
1233       case dir_rt: case dir_lt:  dx = font->idx;  break;
1234       case dir_dn: case dir_up:  dy = font->idy;  break;
1235       }
1236     }
1237     if( x+x0 < 0 ) x0 = -x;
1238     if( y+y0 < 0 ) y0 = -y;
1239     int mx = ww - x;
1240     if( x0 > mx ) x0 = mx;
1241     if( x1 > mx ) x1 = mx;
1242     if( x2 > mx ) x2 = mx;
1243     int my = hh - y;
1244     if( y0 > my ) y0 = my;
1245     if( y1 > my ) y1 = my;
1246     if( y2 > my ) y2 = my;
1247
1248     class pix_t {  /* address char cell */
1249       uint8_t *image_y, *image_u, *image_v, *image_a;
1250       uint8_t *img_y, *img_u, *img_v, *img_a;  int w;
1251     public:
1252       void set_line(int n) {
1253         int ofs = w * n;
1254         img_y = image_y + ofs;
1255         img_u = image_u + ofs;
1256         img_v = image_v + ofs;
1257         img_a = image_a + ofs;
1258       }
1259       pix_t(subtitle_t *subtitle, int x, int y) {
1260         w = subtitle->w;
1261         int ofs = y*w + x;
1262         image_y = subtitle->image_y + ofs;
1263         image_u = subtitle->image_u + ofs;
1264         image_v = subtitle->image_v + ofs;
1265         image_a = subtitle->image_a + ofs;
1266       }
1267       void write(int i, uint32_t yuva) {
1268         img_y[i] = yuva >> 24;
1269         img_u[i] = yuva >> 16;
1270         img_v[i] = yuva >> 8;
1271         img_a[i] = yuva >> 0;
1272       }
1273       void fill_box(uint32_t yuva, int x0,int y0, int x1,int y1) {
1274         if( x0 >= x1 ) return;
1275         while( y0 < y1 ) {
1276           set_line(y0++);
1277           for( int ix=x0; ix<x1; ++ix ) write(ix,yuva);
1278         }
1279       }
1280     } pix(subtitle, x+bw, y+bw); /* origin inside border */
1281     /* dont fill left of cell */
1282     pix.fill_box(bg_yuva,  0, 0, x2,y0);  /* fill top */
1283     pix.fill_box(bg_yuva,  0,y0, x0,y1);  /* fill left */
1284     pix.fill_box(bg_yuva, x1,y0, x2,y1);  /* fill right */
1285     pix.fill_box(bg_yuva,  0,y1, x2,y2);  /* fill bottom */
1286
1287     int wsz = (cp->right-cp->left+7) / 8;
1288     int rx = x0 - ix0;
1289     int ry = y0 - iy0;
1290     /* render char box */
1291     for( int iy=y0; iy<y1; ++iy,++ry ) {
1292       pix.set_line(iy);
1293       uint8_t *row = &bmap[ry*wsz];
1294       int i = rx, ix = x0;
1295       /* dont render bg left of cell */
1296       while( ix < 0 ) {
1297         if( ((row[i>>3] >> (i&7)) & 1) ) pix.write(ix, fg_yuva);
1298         ++ix;  ++i;
1299       }
1300       /* render fg/bg inside of cell */
1301       while( ix < x1 ) {
1302         pix.write(ix, ((row[i>>3] >> (i&7)) & 1) ? fg_yuva : bg_yuva);
1303         ++ix;  ++i;
1304       }
1305     }
1306     if( chr->underline ) {
1307       int uy0 = font->imy;
1308       if( uy0 < y2 ) {
1309         int uy1 = uy0 + (y2+23)/24;
1310         if( uy1 > y2 ) uy1 = y2;
1311         pix.fill_box(fg_yuva, 0,uy0, x2,uy1);
1312       }
1313     }
1314   }
1315   else {
1316     /* no bitmap and not blank */
1317     switch( pdir ) {
1318     case dir_rt:  dx = font->idx;  dy = 0;  break;
1319     case dir_lt:  dx = font->idy;  dy = 0;  break;
1320     case dir_dn:  dx = 0;  dy = font->idy;  break;
1321     case dir_up:  dx = 0;  dy = font->idy;  break;
1322     }
1323   }
1324   x += dx;  y += dy;
1325 }
1326
1327 void zwin_t::
1328 hbar(int x, int y, int ww, int hh, int lm, int rm, int clr)
1329 {
1330 /* w,h = widget dims, ww,hh = bar dims */
1331 /* x,y = widget coord, lm,rm = lt/rt miter, clr = color */
1332   int w = subtitle->w;
1333   int h = subtitle->h;
1334   if( y < 0 ) { hh += y;  y = 0; }
1335   if( y+hh > h ) hh = h - y;
1336   uint32_t yuva = border_yuva;
1337   for( int ih=hh; --ih>=0; ++y ) {
1338     int iw = ww, ix = x;
1339     if( ix < 0 ) { iw += ix;  ix = 0; }
1340     if( ix+iw > w ) iw = w - ix;
1341     int ofs = w*y + ix;
1342     uint8_t *img_y = &subtitle->image_y[ofs];
1343     uint8_t *img_u = &subtitle->image_u[ofs];
1344     uint8_t *img_v = &subtitle->image_v[ofs];
1345     uint8_t *img_a = &subtitle->image_a[ofs];
1346     while( --iw >= 0 ) {
1347       *img_y = clr;         ++img_y;
1348       *img_u = yuva >> 16;  ++img_u;
1349       *img_v = yuva >> 8;   ++img_v;
1350       *img_a = yuva >> 0;   ++img_a;
1351     }
1352     x += lm;  ww -= lm-rm;
1353   }
1354 }
1355
1356 void zwin_t::
1357 vbar(int x, int y, int ww, int hh, int tm, int bm, int clr)
1358 {
1359 /* w,h = widget dims, ww,hh = bar dims */
1360 /* x,y = widget coord, lm,rm = lt/rt miter, clr = color */
1361   int w = subtitle->w;
1362   int h = subtitle->h;
1363   if( x < 0 ) { ww += x;  x = 0; }
1364   if( x+ww > w ) ww = w - x;
1365   uint32_t yuva = border_yuva;
1366   for( int iw=ww; --iw>=0; ++x ) {
1367     int ih = hh, iy = y;
1368     if( iy < 0 ) { ih += iy;  iy = 0; }
1369     if( iy+ih > h ) ih = h - iy;
1370     int ofs = w*iy + x;
1371     uint8_t *img_y = &subtitle->image_y[ofs];
1372     uint8_t *img_u = &subtitle->image_u[ofs];
1373     uint8_t *img_v = &subtitle->image_v[ofs];
1374     uint8_t *img_a = &subtitle->image_a[ofs];
1375     while( --ih >= 0 ) {
1376       *img_y = clr;         img_y += w;
1377       *img_u = yuva >> 16;  img_u += w;
1378       *img_v = yuva >> 8;   img_v += w;
1379       *img_a = yuva >> 0;   img_a += w;
1380     }
1381     y += tm;  hh -= tm-bm;
1382   }
1383 }
1384
1385 void zwin_t::
1386 border(int ilt, int olt, int irt, int ort,
1387        int iup, int oup, int idn, int odn)
1388 {
1389 /* i/o = inside/outside + lt/rt/up/dn = colors */
1390   if( !bw || !subtitle ) return;
1391   int w = subtitle->w;
1392   int h = subtitle->h;
1393   int sbw = bw * scale;
1394   int zo = sbw / 2;  // outside width
1395   int zi = sbw - zo;  // inside width
1396   hbar(0,0,w,zo,1,-1,oup);
1397   hbar(zo,zo,w-2*zo,zi,1,-1,iup);
1398   hbar(sbw-1,h-sbw,w-2*sbw+2,zi,-1,1,idn);
1399   hbar(zo-1,h-zo,w-2*zo+2,zo,-1,1,odn);
1400   vbar(0,1,zo,h-2,1,-1,olt);
1401   vbar(zo,zo+1,zi,h-2*zo-2,1,-1,ilt);
1402   vbar(w-sbw,sbw,zi,h-2*sbw,-1,1,irt);
1403   vbar(w-zo,zo,zo,h-2*zo,-1,1,ort);
1404 }
1405
1406 void zwin_t::
1407 render()
1408 {
1409   if( st < st_visible || !bfr ) {
1410     if( subtitle ) subtitle->draw = -1;
1411     return;
1412   }
1413   /* get bounding box */
1414   int wd = 2*bw;
1415   int ht = 2*bw;
1416   int fw = 0, fh = 0, zz = 0;
1417   switch( pdir ) {
1418   case dir_rt:
1419   case dir_lt:
1420     for( int iy=0; iy<ch; ++iy ) {
1421       int ww = 0;
1422       for( int ix=0; ix<cw; ++ix ) {
1423         chr_t *chr = &bfr[iy*MX_CX + ix];
1424         bitfont_t *font = chr_font(chr, scale);
1425         if( font->idy > fh ) fh = font->idy;
1426         int b = chr->glyph;
1427         if( b >= font->nch ) continue;
1428         bitfont_t::bitchar_t *cp = &font->chrs[b];
1429         int sz = !cp->bitmap ? font->idx :
1430           pdir == dir_rt ? cp->right : cp->left;
1431         ww += sz;
1432       }
1433       if( ww > zz ) zz = ww;
1434     }
1435     wd += zz;
1436     ht += ch * fh;
1437     break;
1438   case dir_up:
1439   case dir_dn:
1440     for( int ix=0; ix<cw; ++ix ) {
1441       int hh = 0;
1442       for( int iy=0; iy<ch; ++iy ) {
1443         chr_t *chr = &bfr[iy*MX_CX + ix];
1444         bitfont_t *font = chr_font(chr, scale);
1445         if( font->idx > fw ) fw = font->idx;
1446         int b = chr->glyph;
1447         if( b >= font->nch ) continue;
1448         bitfont_t::bitchar_t *cp = &font->chrs[b];
1449         int sz = !cp->bitmap ? font->idy :
1450           pdir == dir_dn ? cp->decent : cp->ascent;
1451         hh += sz;
1452       }
1453       if( hh > zz ) zz = hh;
1454     }
1455     wd += cw * fw;
1456     ht += zz;
1457     break;
1458   }
1459   video_t *video = svc->cc->video;
1460 //zmsgs(" an_horz=%d, an_vert=%d, an_rela=%d, %dx%d\n",
1461 //  anchor_horizontal, anchor_vertical, anchor_relative,
1462 //  video->coded_picture_width, video->coded_picture_height);
1463   int ax = ((anchor_horizontal+10) * video->coded_picture_width) / 200;
1464   int ay = ((anchor_vertical+10) * video->coded_picture_height) / 100;
1465   if( anchor_relative ) { ax += ax; ay += ay; }
1466   switch( anchor_point ) {
1467   case 0x00:                            break;  /* nw */
1468   case 0x01:  ax -= wd/2;               break;  /* n */
1469   case 0x02:  ax -= wd;                 break;  /* ne */
1470   case 0x03:  ax -= wd;    ay -= ht/2;  break;  /* e */
1471   case 0x04:  ax -= wd;    ay -= ht;    break;  /* se */
1472   case 0x05:  ax -= wd/2;  ay -= ht;    break;  /* s */
1473   case 0x06:               ay -= ht;    break;  /* sw */
1474   case 0x07:               ay -= ht/2;  break;  /* w */
1475   case 0x08:  ax -= wd/2;  ay -= ht/2;  break;  /* center */
1476   }
1477
1478   zmpeg3_t *src = video->src;
1479   strack_t *strack = src->create_strack(svc->ctrk, video);
1480   if( !subtitle )
1481     subtitle = new subtitle_t(id, wd, ht);
1482   if( subtitle->done < 0 ) {
1483     if( strack->append_subtitle(subtitle,0) ) {
1484       delete subtitle;  subtitle = 0;
1485       return;
1486     }
1487   }
1488   subtitle->set_image_size(wd, ht);
1489   subtitle->x1 = ax;  subtitle->x2 = ax + wd;
1490   subtitle->y1 = ay;  subtitle->y2 = ay + ht;
1491   subtitle->draw = 1;
1492   subtitle->frame_time = video->frame_time;
1493 //print_buffer();
1494   uint8_t yy = fill_yuva >> 24;
1495   uint8_t uu = fill_yuva >> 16;
1496   uint8_t vv = fill_yuva >> 8;
1497   uint8_t aa = fill_yuva >> 0;
1498   aa = 0xff - aa;  // this is probably wrong
1499   int w = subtitle->w;
1500   int h = subtitle->h;
1501   int sz = w * h;
1502   memset(subtitle->image_y, yy, sz);
1503   memset(subtitle->image_u, uu, sz);
1504   memset(subtitle->image_v, vv, sz);
1505   memset(subtitle->image_a, aa, sz);
1506   yy = border_yuva >> 24;
1507   int hi_y, lo_y;
1508   if( (hi_y = yy + 0x60) > 0xff ) hi_y = 0xff;
1509   if( (lo_y = yy - 0x60) < 0x10 ) lo_y = 0x10;
1510   switch( border_type ) {
1511   case 0x01: /* raised */
1512     border(hi_y, yy, hi_y, yy, hi_y, yy, hi_y, yy);
1513     break;
1514   case 0x02: /* depressed */
1515     border(lo_y, yy, lo_y, yy, lo_y, yy, lo_y, yy);
1516     break;
1517   case 0x03: /* uniform */
1518     border(yy, yy, yy, yy, yy, yy, yy, yy);
1519     break;
1520   case 0x04: /* shadowl */
1521     border(lo_y, lo_y, hi_y, hi_y, hi_y, lo_y, hi_y, lo_y);
1522     break;
1523   case 0x05: /* shadowr */
1524     border(hi_y, hi_y, lo_y, lo_y, hi_y, lo_y, hi_y, lo_y);
1525     break;
1526   default:
1527   case 0x00: /* none */
1528     break;
1529   }
1530   /* render character data */
1531   switch( pdir ) {
1532   case dir_rt:
1533     for( int iy=0; iy<ch; ++iy ) {
1534       x = 0;  y = iy * fh;
1535       chr_t *bp = &bfr[iy * MX_CX];
1536       for( int ix=0; ix<cw; ++ix, ++bp ) {
1537         put_chr(bp);
1538       }
1539     }
1540     break;
1541   case dir_lt:
1542     for( int iy=0; iy<ch; ++iy ) {
1543       x = w;  y = iy * fh;
1544       chr_t *bp = &bfr[iy*MX_CX + cw];
1545       for( int ix=cw; --ix>=0; )
1546         put_chr(--bp);
1547     }
1548     break;
1549   case dir_dn:
1550     for( int ix=0; ix<cw; ++ix ) {
1551       x = ix * fw;  y = 0;
1552       chr_t *bp = &bfr[ix];
1553       for( int iy=0; iy<ch; ++iy, bp+=MX_CX )
1554         put_chr(bp);
1555     }
1556     break;
1557   case dir_up:
1558     for( int ix=0; ix<cw; ++ix ) {
1559       x = ix * fw;  y = h;
1560       chr_t *bp = &bfr[ch*MX_CX - cw + ix];
1561       for( int iy=ch; --iy>=0; ) {
1562         put_chr(bp -= MX_CX);
1563       }
1564     }
1565     break;
1566   }
1567 }
1568