Add horizontal and vertical subtitle offset corrections. Some cleanup
[vlc.git] / modules / codec / ogt / common.c
1 /*****************************************************************************
2  * Common SVCD and VCD subtitle routines.
3  *****************************************************************************
4  * Copyright (C) 2003, 2004 VideoLAN
5  * $Id: common.c,v 1.8 2004/01/16 13:32:37 rocky Exp $
6  *
7  * Author: Rocky Bernstein
8  *   based on code from:
9  *       Julio Sanchez Fernandez (http://subhandler.sourceforge.net)
10  *       Samuel Hocevar <sam@zoy.org>
11  *       Laurent Aimar <fenrir@via.ecp.fr>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
26  *****************************************************************************/
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31 #include <vlc/vlc.h>
32 #include <vlc/vout.h>
33 #include <vlc/decoder.h>
34
35 #include "subtitle.h"
36 #include "pixmap.h"
37 #include "common.h"
38 #ifdef HAVE_LIBPNG
39 #include "write_png.h"
40 #endif
41
42 /*****************************************************************************
43  Free Resources associated with subtitle packet.
44  *****************************************************************************/
45 void VCDSubClose( vlc_object_t *p_this )
46 {
47     decoder_t     *p_dec = (decoder_t*)p_this;
48     decoder_sys_t *p_sys = p_dec->p_sys;
49
50     dbg_print( (DECODE_DBG_CALL|DECODE_DBG_EXT) , "");
51
52     if( !p_sys->b_packetizer )
53     {
54         /* FIXME check if it's ok to not lock vout */
55         if( p_sys->p_vout != NULL && p_sys->p_vout->p_subpicture != NULL )
56         {
57             subpicture_t *  p_subpic;
58             int             i_subpic;
59
60             for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
61             {
62                 p_subpic = &p_sys->p_vout->p_subpicture[i_subpic];
63
64                 if( p_subpic != NULL &&
65                     ( ( p_subpic->i_status == RESERVED_SUBPICTURE ) ||
66                       ( p_subpic->i_status == READY_SUBPICTURE ) ) )
67                 {
68                     vout_DestroySubPicture( p_sys->p_vout, p_subpic );
69                 }
70             }
71         }
72     }
73
74     if( p_sys->p_block )
75     {
76         block_ChainRelease( p_sys->p_block );
77     }
78
79     free( p_sys );
80 }
81
82 /*****************************************************************************
83
84 Initialize so the next packet will start off a new one.
85
86  *****************************************************************************/
87 void 
88 VCDSubInitSubtitleBlock( decoder_sys_t * p_sys ) 
89 {
90   p_sys->i_spu_size = 0;
91   p_sys->state      = SUBTITLE_BLOCK_EMPTY;
92   p_sys->i_spu      = 0;
93   p_sys->p_block    = NULL;
94   p_sys->subtitle_data_pos = 0;
95
96 }
97
98 void 
99 VCDSubInitSubtitleData(decoder_sys_t *p_sys)
100 {
101   if ( p_sys->subtitle_data ) {
102     if ( p_sys->subtitle_data_size < p_sys->i_spu_size ) {
103       p_sys->subtitle_data = realloc(p_sys->subtitle_data,
104                                     p_sys->i_spu_size);
105       p_sys->subtitle_data_size = p_sys->i_spu_size;
106     }
107   } else {
108     p_sys->subtitle_data = malloc(p_sys->i_spu_size);
109     p_sys->subtitle_data_size = p_sys->i_spu_size;
110     /* FIXME: wrong place to get p_sys */
111     p_sys->i_image = 0;
112   }
113   p_sys->subtitle_data_pos = 0;
114 }
115
116 void 
117 VCDSubAppendData ( decoder_t *p_dec, uint8_t *buffer, uint32_t buf_len )
118 {
119   decoder_sys_t *p_sys = p_dec->p_sys;
120   int chunk_length = buf_len;
121
122   if ( chunk_length > p_sys->i_spu_size - p_sys->subtitle_data_pos ) {
123     msg_Warn( p_dec, "too much data (%d) expecting at most %u",
124               chunk_length, p_sys->i_spu_size - p_sys->subtitle_data_pos );
125
126     chunk_length = p_sys->i_spu_size - p_sys->subtitle_data_pos;
127   }
128
129   if ( chunk_length > 0 ) {
130 #if 0
131     int i;
132     int8_t *b=buffer;
133     for (i=0; i<chunk_length; i++)
134       printf ("%02x", b[i]);
135     printf("\n");
136 #endif
137     
138     memcpy(p_sys->subtitle_data + p_sys->subtitle_data_pos,
139            buffer, chunk_length);
140     p_sys->subtitle_data_pos += chunk_length;
141     dbg_print(DECODE_DBG_PACKET, "%d bytes appended, pointer now %d",
142               chunk_length, p_sys->subtitle_data_pos);
143   }
144 }
145
146
147 /*****************************************************************************
148  * FindVout: Find a vout or wait for one to be created.
149  *****************************************************************************/
150 vout_thread_t *VCDSubFindVout( decoder_t *p_dec )
151 {
152     vout_thread_t *p_vout = NULL;
153
154     /* Find an available video output */
155     do
156     {
157         if( p_dec->b_die || p_dec->b_error )
158         {
159             break;
160         }
161
162         p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE );
163         if( p_vout )
164         {
165             break;
166         }
167
168         msleep( VOUT_OUTMEM_SLEEP );
169     }
170     while( 1 );
171
172     return p_vout;
173 }
174
175
176
177 /**
178    Remove color palette by expanding pixel entries to contain the
179    palette values. We work from the free space at the end to the
180    beginning so we can expand inline.
181 */
182 static void
183 InlinePalette ( /*inout*/ uint8_t *p_dest, decoder_sys_t *p_sys )
184 {
185   const unsigned int i_width  = p_sys->i_width;
186   const unsigned int i_height = p_sys->i_height;
187   int n = (i_height * i_width) - 1;
188   uint8_t    *p_from = p_dest;
189   ogt_yuvt_t *p_to   = (ogt_yuvt_t *) p_dest;
190   
191   for ( ; n >= 0 ; n-- ) {
192     p_to[n] = p_sys->p_palette[p_from[n]];
193     /*p_to[n] = p_sys->p_palette[p_from[3]];*/
194   }
195 }
196
197 /**
198    Check to see if user has overridden subtitle aspect ratio. 
199    0 is returned for no override which means just counteract any
200    scaling effects.
201 */
202 unsigned int 
203 VCDSubGetAROverride(vlc_object_t * p_input, vout_thread_t *p_vout)
204 {
205   char *psz_string = config_GetPsz( p_input, MODULE_STRING "-aspect-ratio" );
206
207   /* Check whether the user tried to override aspect ratio */
208   if( !psz_string ) return 0;
209
210   {
211     unsigned int i_new_aspect = 0;
212     char *psz_parser = strchr( psz_string, ':' );
213     
214     if( psz_parser )
215       {
216         *psz_parser++ = '\0';
217         i_new_aspect = atoi( psz_string ) * VOUT_ASPECT_FACTOR
218           / atoi( psz_parser );
219       }
220     else
221       {
222         i_new_aspect = p_vout->output.i_width * VOUT_ASPECT_FACTOR
223           * atof( psz_string )
224           / p_vout->output.i_height;
225       }
226     
227     return i_new_aspect;
228   }
229 }
230
231
232 /**
233    Scales down (reduces size) of p_dest in the x direction as 
234    determined through aspect ratio x_scale by y_scale. Scaling
235    is done in place. p_spu->i_width, is updated to new width
236
237    The aspect ratio is assumed to be between 1/2 and 1.
238
239    Note: the scaling truncates the new width rather than rounds it.
240    Perhaps something one might want to address.
241 */
242 void
243 VCDSubScaleX( decoder_t *p_dec, subpicture_t *p_spu, 
244               unsigned int i_scale_x, unsigned int i_scale_y )
245 {
246   int i_row, i_col;
247
248   decoder_sys_t *p_sys = p_dec->p_sys;
249   uint8_t *p_src1 = p_spu->p_sys->p_data;
250   uint8_t *p_src2 = p_src1 + PIXEL_SIZE;
251   uint8_t *p_dst  = p_src1;
252   unsigned int i_new_width = (p_spu->i_width * i_scale_x) / i_scale_y ;
253   unsigned int i_used=0;  /* Number of bytes used up in p_src1. */
254
255   dbg_print( (DECODE_DBG_CALL|DECODE_DBG_TRANSFORM) , 
256              "aspect ratio %i:%i, Old width: %d, new width: %d", 
257              i_scale_x, i_scale_y, p_spu->i_width, i_new_width);
258
259   if (! (i_scale_x < i_scale_y && i_scale_y < i_scale_x+i_scale_x) )
260     {
261       msg_Warn( p_dec, "Need x < y < 2x. x: %i, y: %i", i_scale_x, i_scale_y );
262       return;
263     }
264   
265   for ( i_row=0; i_row <= p_spu->i_height - 1; i_row++ ) {
266
267     if (i_used != 0) {
268       /* Discard the remaining piece of the column of the previous line*/
269       i_used=0;
270       p_src1 = p_src2;
271       p_src2 += PIXEL_SIZE;
272     }
273     
274     for ( i_col=0; i_col <= p_spu->i_width - 2; i_col++ ) {
275       unsigned int i;
276       unsigned int w1= i_scale_x - i_used;
277       unsigned int w2;
278       
279       if ( i_scale_y - w1 <= i_scale_x ) {
280         /* Average spans 2 pixels. */
281         w2 = i_scale_y - w1;
282
283         for (i = 0; i < PIXEL_SIZE; i++ ) {
284           *p_dst = ( (*p_src1 * w1) + (*p_src2 * w2) ) / i_scale_y;
285           p_src1++; p_src2++; p_dst++;
286         }
287       } else {
288         /* Average spans 3 pixels. */
289         unsigned int w0 = w1;
290         unsigned int w1 = i_scale_x;
291         uint8_t *p_src0 = p_src1;
292         w2 = i_scale_y - w0 - w1;
293         p_src1 = p_src2;
294         p_src2 += PIXEL_SIZE;
295         
296         for (i = 0; i < PIXEL_SIZE; i++ ) {
297           *p_dst = ( (*p_src0 * w0) + (*p_src1 * w1) + (*p_src2 * w2) ) 
298                      / i_scale_y;
299           p_src0++; p_src1++; p_src2++; p_dst++;
300         }
301         i_col++;
302       }
303
304       i_used = w2;
305
306       if (i_scale_x == i_used) {
307         /* End of last pixel was end of p_src2. */
308         p_src1 = p_src2;
309         p_src2 += PIXEL_SIZE;
310         i_col++;
311         i_used = 0;
312       }
313     }
314   }
315   p_spu->i_width = i_new_width;
316
317   if ( p_sys && p_sys->i_debug & DECODE_DBG_TRANSFORM )
318   { 
319     ogt_yuvt_t *p_source = (ogt_yuvt_t *) p_spu->p_sys->p_data;
320     for ( i_row=0; i_row < p_spu->i_height; i_row++ ) {
321       for ( i_col=0; i_col < p_spu->i_width; i_col++ ) {
322         printf("%1x", p_source->s.t);
323         p_source++;
324       }
325       printf("\n");
326     }
327   }
328
329 }
330
331 /**
332    The video may be scaled. However subtitle bitmaps assume an 1:1
333    aspect ratio. So unless the user has specified otherwise, we
334    need to scale to compensate for or undo the effects of video
335    output scaling.
336    
337    Perhaps this should go in the Render routine? The advantage would
338    be that it will deal with a dynamically changing aspect ratio.
339    The downside is having to scale many times for each render call.
340
341    We also expand palette entries here, unless we are dealing with a 
342    palettized chroma (e.g. RGB2).
343 */
344
345 void 
346 VCDSubHandleScaling( subpicture_t *p_spu, decoder_t *p_dec )
347 {
348   vlc_object_t * p_input = p_spu->p_sys->p_input;
349   vout_thread_t *p_vout = vlc_object_find( p_input, VLC_OBJECT_VOUT, 
350                                            FIND_CHILD );
351   unsigned int i_aspect_x, i_aspect_y;
352   uint8_t *p_dest = (uint8_t *)p_spu->p_sys->p_data;
353
354   if (p_vout) {
355     /* Check for user-configuration override. */
356     unsigned int i_new_aspect;
357     
358     if ( p_vout->output.i_chroma == VLC_FOURCC('R','G','B','2') ) {
359       /* This is an unscaled palettized format. We don't allow 
360          user scaling here. And to make the render process faster,
361          we don't expand the palette entries into a color value.
362        */
363       return;
364     }
365         
366     InlinePalette( p_dest, p_dec->p_sys );
367     i_new_aspect = VCDSubGetAROverride( p_input, p_vout );
368
369     if (i_new_aspect == VOUT_ASPECT_FACTOR) {
370       /* For scaling 1:1, nothing needs to be done. Note this means
371          subtitles will get scaled the same way the video does.
372       */
373       ;
374     } else {
375       if (0 == i_new_aspect) {
376         /* Counteract the effects of background video scaling when
377            there is scaling. That's why x and y are reversed from
378            the else branch in the call below.
379         */
380         switch( p_vout->output.i_chroma )
381           {
382             /* chromas which are not scaled: */
383           case VLC_FOURCC('I','4','2','0'):
384           case VLC_FOURCC('I','Y','U','V'):
385           case VLC_FOURCC('Y','V','1','2'):
386           case VLC_FOURCC('Y','U','Y','2'):
387             break;
388             
389             /* chromas which are scaled: */
390           case VLC_FOURCC('R','V','1','6'):
391           case VLC_FOURCC('R','V','2','4'):
392           case VLC_FOURCC('R','V','3','2'):
393           case VLC_FOURCC('R','G','B','2'):
394             return;
395             break;
396             
397           default:
398             msg_Err( p_vout, "unknown chroma %x", 
399                      p_vout->output.i_chroma );
400             return;
401             break;
402           }
403         /* We get here only for scaled chromas. */
404         vout_AspectRatio( p_vout->render.i_aspect, &i_aspect_y, 
405                           &i_aspect_x );
406       } else {
407         /* User knows best? */
408         vout_AspectRatio( i_new_aspect, &i_aspect_x, &i_aspect_y );
409       }
410       VCDSubScaleX( p_dec, p_spu, i_aspect_x, i_aspect_y );
411     }
412   }
413 }
414
415
416 /**
417  * DestroySPU: subpicture destructor
418  */
419 void VCDSubDestroySPU( subpicture_t *p_spu )
420 {
421     if( p_spu->p_sys->p_input )
422     {
423         /* Detach from our input thread */
424         vlc_object_release( p_spu->p_sys->p_input );
425     }
426
427     vlc_mutex_destroy( &p_spu->p_sys->lock );
428     free( p_spu->p_sys );
429 }
430
431 /*****************************************************************************
432   This callback is called from the input thread when we need cropping
433  *****************************************************************************/
434 int VCDSubCropCallback( vlc_object_t *p_object, char const *psz_var,
435                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
436 {
437     VCDSubUpdateSPU( (subpicture_t *)p_data, p_object );
438
439     return VLC_SUCCESS;
440 }
441
442
443 /*****************************************************************************
444   update subpicture settings
445  *****************************************************************************
446   This function is called from CropCallback and at initialization time, to
447   retrieve crop information from the input.
448  *****************************************************************************/
449 void VCDSubUpdateSPU( subpicture_t *p_spu, vlc_object_t *p_object )
450 {
451     vlc_value_t val;
452
453     p_spu->p_sys->b_crop = val.b_bool;
454     if( !p_spu->p_sys->b_crop )
455     {
456         return;
457     }
458
459     if ( VLC_SUCCESS == var_Get( p_object, "x-start", &val ) )
460       p_spu->p_sys->i_x_start = val.i_int;
461     if ( VLC_SUCCESS == var_Get( p_object, "y-start", &val ) )
462       p_spu->p_sys->i_y_start = val.i_int;
463     if ( VLC_SUCCESS == var_Get( p_object, "x-end", &val ) )
464       p_spu->p_sys->i_x_end = val.i_int;
465     if ( VLC_SUCCESS == var_Get( p_object, "y-end", &val ) )
466       p_spu->p_sys->i_y_end = val.i_int;
467
468 }
469
470 /* 
471    Dump an a subtitle image to standard output - for debugging.
472  */
473 void VCDSubDumpImage( uint8_t *p_image, uint32_t i_height, uint32_t i_width )
474 {
475   uint8_t *p = p_image;
476   unsigned int i_row;    /* scanline row number */
477   unsigned int i_column; /* scanline column number */
478
479   printf("-------------------------------------\n++");
480   for ( i_row=0; i_row < i_height; i_row ++ ) {
481     for ( i_column=0; i_column<i_width; i_column++ ) {
482       printf("%1d", *p++ & 0x03);
483     }
484     printf("\n++");
485   }
486   printf("\n-------------------------------------\n");
487 }
488
489 /***************************************************
490    Color conversion from
491     http://www.inforamp.net/~poynton/notes/colour_and_gamma/ColorFAQ.html#RTFToC30
492     http://people.ee.ethz.ch/~buc/brechbuehler/mirror/color/ColorFAQ.html
493  
494     Thanks to Billy Biggs <vektor@dumbterm.net> for the pointer and
495     the following conversion.
496  
497     R' = [ 1.1644         0    1.5960 ]   ([ Y' ]   [  16 ])
498     G' = [ 1.1644   -0.3918   -0.8130 ] * ([ Cb ] - [ 128 ])
499     B' = [ 1.1644    2.0172         0 ]   ([ Cr ]   [ 128 ])
500
501   See also vlc/modules/video_chroma/i420_rgb.h and
502   vlc/modules/video_chroma/i420_rgb_c.h for a way to do this in a way
503   more optimized for integer arithmetic. Would be nice to merge the
504   two routines.
505  
506 ***************************************************/
507
508 #ifdef HAVE_LIBPNG
509
510 #define BYTES_PER_RGB 3
511 #define PALETTE_SIZE  4
512 /* Note the below assumes the above is a power of 2 */
513 #define PALETTE_SIZE_MASK (PALETTE_SIZE-1)
514
515 /* 
516    Dump an a subtitle image to a Portable Network Graphics (PNG) file.
517    All we do here is convert YUV palette entries to RGB, expand
518    the image into a linear RGB pixel array, and call the routine
519    that does the PNG writing.
520  */
521
522 void 
523 VCDSubDumpPNG( uint8_t *p_image, decoder_t *p_dec,
524                uint32_t i_height, uint32_t i_width, const char *filename,
525                png_text *text_ptr, int i_text_count )
526 {
527   decoder_sys_t *p_sys = p_dec->p_sys;
528   uint8_t *p = p_image;
529   uint8_t *image_data = malloc(BYTES_PER_RGB * i_height * i_width );
530   uint8_t *q = image_data;
531   unsigned int i_row;    /* scanline row number */
532   unsigned int i_column; /* scanline column number */
533   uint8_t rgb_palette[PALETTE_SIZE * BYTES_PER_RGB];
534   int i;
535
536   dbg_print( (DECODE_DBG_CALL), "%s", filename);
537   
538   if (NULL == image_data) return;
539
540   /* Convert palette YUV into RGB. */
541   for (i=0; i<PALETTE_SIZE; i++) {
542     ogt_yuvt_t *p_yuv     = &(p_sys->p_palette[i]);
543     uint8_t   *p_rgb_out  = &(rgb_palette[i*BYTES_PER_RGB]);
544     yuv2rgb( p_yuv, p_rgb_out );
545   }
546   
547   /* Convert palette entries into linear RGB array. */
548   for ( i_row=0; i_row < i_height; i_row ++ ) {
549     for ( i_column=0; i_column<i_width; i_column++ ) {
550       uint8_t *p_rgb = &rgb_palette[ ((*p)&PALETTE_SIZE_MASK)*BYTES_PER_RGB ];
551       *q++ = p_rgb[0];
552       *q++ = p_rgb[1];
553       *q++ = p_rgb[2];
554       p++;
555     }
556   }
557   
558   write_png( filename, i_height, i_width, image_data, text_ptr, i_text_count );
559   free(image_data);
560 }
561 #endif /*HAVE_LIBPNG*/
562
563 \f
564 /* 
565  * Local variables:
566  *  c-file-style: "gnu"
567  *  tab-width: 8
568  *  indent-tabs-mode: nil
569  * End:
570  */