* src/video_output/video_output.c, modules/codec/ogt: got rid of vout_AspectRatio().
[vlc.git] / modules / codec / ogt / common.c
1 /*****************************************************************************
2  * Common SVCD and CVD subtitle routines.
3  *****************************************************************************
4  * Copyright (C) 2003, 2004 VideoLAN
5  * $Id$
6  *
7  * Author: Rocky Bernstein <rocky@panix.com>
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->subtitle_data);
80     free( p_sys );
81 }
82
83 /*****************************************************************************
84
85 Initialize so the next packet will start off a new one.
86
87  *****************************************************************************/
88 void 
89 VCDSubInitSubtitleBlock( decoder_sys_t * p_sys ) 
90 {
91   p_sys->i_spu_size = 0;
92   p_sys->state      = SUBTITLE_BLOCK_EMPTY;
93   p_sys->i_spu      = 0;
94   p_sys->p_block    = NULL;
95   p_sys->subtitle_data_pos = 0;
96
97 }
98
99 void 
100 VCDSubInitSubtitleData(decoder_sys_t *p_sys)
101 {
102   if ( p_sys->subtitle_data ) {
103     if ( p_sys->subtitle_data_size < p_sys->i_spu_size ) {
104       p_sys->subtitle_data = realloc(p_sys->subtitle_data,
105                                     p_sys->i_spu_size);
106       p_sys->subtitle_data_size = p_sys->i_spu_size;
107     }
108   } else {
109     p_sys->subtitle_data = malloc(p_sys->i_spu_size);
110     p_sys->subtitle_data_size = p_sys->i_spu_size;
111     /* FIXME: wrong place to get p_sys */
112     p_sys->i_image = 0;
113   }
114   p_sys->subtitle_data_pos = 0;
115 }
116
117 void 
118 VCDSubAppendData ( decoder_t *p_dec, uint8_t *buffer, uint32_t buf_len )
119 {
120   decoder_sys_t *p_sys = p_dec->p_sys;
121   int chunk_length = buf_len;
122
123   if ( chunk_length > p_sys->i_spu_size - p_sys->subtitle_data_pos ) {
124     msg_Warn( p_dec, "too much data (%d) expecting at most %u",
125               chunk_length, p_sys->i_spu_size - p_sys->subtitle_data_pos );
126
127     chunk_length = p_sys->i_spu_size - p_sys->subtitle_data_pos;
128   }
129
130   if ( chunk_length > 0 ) {
131 #if 0
132     int i;
133     int8_t *b=buffer;
134     for (i=0; i<chunk_length; i++)
135       printf ("%02x", b[i]);
136     printf("\n");
137 #endif
138     
139     memcpy(p_sys->subtitle_data + p_sys->subtitle_data_pos,
140            buffer, chunk_length);
141     p_sys->subtitle_data_pos += chunk_length;
142     dbg_print(DECODE_DBG_PACKET, "%d bytes appended, pointer now %d",
143               chunk_length, p_sys->subtitle_data_pos);
144   }
145 }
146
147
148 /*****************************************************************************
149  * FindVout: Find a vout or wait for one to be created.
150  *****************************************************************************/
151 vout_thread_t *VCDSubFindVout( decoder_t *p_dec )
152 {
153     vout_thread_t *p_vout = NULL;
154
155     /* Find an available video output */
156     do
157     {
158         if( p_dec->b_die || p_dec->b_error )
159         {
160             break;
161         }
162
163         p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE );
164         if( p_vout )
165         {
166             break;
167         }
168
169         msleep( VOUT_OUTMEM_SLEEP );
170     }
171     while( 1 );
172
173     return p_vout;
174 }
175
176
177
178 /**
179    Remove color palette by expanding pixel entries to contain the
180    palette values. We work from the free space at the end to the
181    beginning so we can expand inline.
182 */
183 static void
184 InlinePalette ( /*inout*/ uint8_t *p_dest, decoder_sys_t *p_sys )
185 {
186   const unsigned int i_width  = p_sys->i_width;
187   const unsigned int i_height = p_sys->i_height;
188   int n = (i_height * i_width) - 1;
189   uint8_t    *p_from = p_dest;
190   ogt_yuvt_t *p_to   = (ogt_yuvt_t *) p_dest;
191   
192   for ( ; n >= 0 ; n-- ) {
193     p_to[n] = p_sys->p_palette[p_from[n]];
194     /*p_to[n] = p_sys->p_palette[p_from[3]];*/
195   }
196 }
197
198 /**
199    Check to see if user has overridden subtitle aspect ratio. 
200    0 is returned for no override which means just counteract any
201    scaling effects.
202 */
203 unsigned int 
204 VCDSubGetAROverride(vlc_object_t * p_input, vout_thread_t *p_vout)
205 {
206   char *psz_string = config_GetPsz( p_input, MODULE_STRING "-aspect-ratio" );
207
208   /* Check whether the user tried to override aspect ratio */
209   if( !psz_string ) return 0;
210
211   {
212     unsigned int i_new_aspect = 0;
213     char *psz_parser = strchr( psz_string, ':' );
214     
215     if( psz_parser )
216       {
217         *psz_parser++ = '\0';
218         i_new_aspect = atoi( psz_string ) * VOUT_ASPECT_FACTOR
219           / atoi( psz_parser );
220       }
221     else
222       {
223         i_new_aspect = p_vout->output.i_width * VOUT_ASPECT_FACTOR
224           * atof( psz_string )
225           / p_vout->output.i_height;
226       }
227     
228     return i_new_aspect;
229   }
230 }
231
232
233 /**
234    Scales down (reduces size) of p_dest in the x direction as 
235    determined through aspect ratio x_scale by y_scale. Scaling
236    is done in place. p_spu->i_width, is updated to new width
237
238    The aspect ratio is assumed to be between 1/2 and 1.
239
240    Note: the scaling truncates the new width rather than rounds it.
241    Perhaps something one might want to address.
242 */
243 void
244 VCDSubScaleX( decoder_t *p_dec, subpicture_t *p_spu, 
245               unsigned int i_scale_x, unsigned int i_scale_y )
246 {
247   int i_row, i_col;
248
249   decoder_sys_t *p_sys = p_dec->p_sys;
250   uint8_t *p_src1 = p_spu->p_sys->p_data;
251   uint8_t *p_src2 = p_src1 + PIXEL_SIZE;
252   uint8_t *p_dst  = p_src1;
253   unsigned int i_new_width = (p_spu->i_width * i_scale_x) / i_scale_y ;
254   unsigned int i_used=0;  /* Number of bytes used up in p_src1. */
255
256   dbg_print( (DECODE_DBG_CALL|DECODE_DBG_TRANSFORM) , 
257              "aspect ratio %i:%i, Old width: %d, new width: %d", 
258              i_scale_x, i_scale_y, p_spu->i_width, i_new_width);
259
260   if (! (i_scale_x < i_scale_y && i_scale_y < i_scale_x+i_scale_x) )
261     {
262       msg_Warn( p_dec, "Need x < y < 2x. x: %i, y: %i", i_scale_x, i_scale_y );
263       return;
264     }
265   
266   for ( i_row=0; i_row <= p_spu->i_height - 1; i_row++ ) {
267
268     if (i_used != 0) {
269       /* Discard the remaining piece of the column of the previous line*/
270       i_used=0;
271       p_src1 = p_src2;
272       p_src2 += PIXEL_SIZE;
273     }
274     
275     for ( i_col=0; i_col <= p_spu->i_width - 2; i_col++ ) {
276       unsigned int i;
277       unsigned int w1= i_scale_x - i_used;
278       unsigned int w2;
279       
280       if ( i_scale_y - w1 <= i_scale_x ) {
281         /* Average spans 2 pixels. */
282         w2 = i_scale_y - w1;
283
284         for (i = 0; i < PIXEL_SIZE; i++ ) {
285           *p_dst = ( (*p_src1 * w1) + (*p_src2 * w2) ) / i_scale_y;
286           p_src1++; p_src2++; p_dst++;
287         }
288       } else {
289         /* Average spans 3 pixels. */
290         unsigned int w0 = w1;
291         unsigned int w1 = i_scale_x;
292         uint8_t *p_src0 = p_src1;
293         w2 = i_scale_y - w0 - w1;
294         p_src1 = p_src2;
295         p_src2 += PIXEL_SIZE;
296         
297         for (i = 0; i < PIXEL_SIZE; i++ ) {
298           *p_dst = ( (*p_src0 * w0) + (*p_src1 * w1) + (*p_src2 * w2) ) 
299                      / i_scale_y;
300           p_src0++; p_src1++; p_src2++; p_dst++;
301         }
302         i_col++;
303       }
304
305       i_used = w2;
306
307       if (i_scale_x == i_used) {
308         /* End of last pixel was end of p_src2. */
309         p_src1 = p_src2;
310         p_src2 += PIXEL_SIZE;
311         i_col++;
312         i_used = 0;
313       }
314     }
315   }
316   p_spu->i_width = i_new_width;
317
318   if ( p_sys && p_sys->i_debug & DECODE_DBG_TRANSFORM )
319   { 
320     ogt_yuvt_t *p_source = (ogt_yuvt_t *) p_spu->p_sys->p_data;
321     for ( i_row=0; i_row < p_spu->i_height; i_row++ ) {
322       for ( i_col=0; i_col < p_spu->i_width; i_col++ ) {
323         printf("%1x", p_source->s.t);
324         p_source++;
325       }
326       printf("\n");
327     }
328   }
329
330 }
331
332 /**
333    The video may be scaled. However subtitle bitmaps assume an 1:1
334    aspect ratio. So unless the user has specified otherwise, we
335    need to scale to compensate for or undo the effects of video
336    output scaling.
337    
338    Perhaps this should go in the Render routine? The advantage would
339    be that it will deal with a dynamically changing aspect ratio.
340    The downside is having to scale many times for each render call.
341
342    We also expand palette entries here, unless we are dealing with a 
343    palettized chroma (e.g. RGB2).
344 */
345
346 void 
347 VCDSubHandleScaling( subpicture_t *p_spu, decoder_t *p_dec )
348 {
349   vlc_object_t * p_input = p_spu->p_sys->p_input;
350   vout_thread_t *p_vout = vlc_object_find( p_input, VLC_OBJECT_VOUT, 
351                                            FIND_CHILD );
352   int i_aspect_x, i_aspect_y;
353   uint8_t *p_dest = (uint8_t *)p_spu->p_sys->p_data;
354
355   if (p_vout) {
356     /* Check for user-configuration override. */
357     unsigned int i_new_aspect;
358     
359     if ( p_vout->output.i_chroma == VLC_FOURCC('R','G','B','2') ) {
360       /* This is an unscaled palettized format. We don't allow 
361          user scaling here. And to make the render process faster,
362          we don't expand the palette entries into a color value.
363        */
364       return;
365     }
366         
367     InlinePalette( p_dest, p_dec->p_sys );
368     i_new_aspect = VCDSubGetAROverride( p_input, p_vout );
369
370     if (i_new_aspect == VOUT_ASPECT_FACTOR) {
371       /* For scaling 1:1, nothing needs to be done. Note this means
372          subtitles will get scaled the same way the video does.
373       */
374       ;
375     } else {
376       if (0 == i_new_aspect) {
377         /* Counteract the effects of background video scaling when
378            there is scaling. That's why x and y are reversed from
379            the else branch in the call below.
380         */
381         switch( p_vout->output.i_chroma )
382           {
383             /* chromas in which scaling is done outside of our
384                blending routine, so we need to compensate for those
385                effects before blending gets called: */
386           case VLC_FOURCC('I','4','2','0'):
387           case VLC_FOURCC('I','Y','U','V'):
388           case VLC_FOURCC('Y','V','1','2'):
389           case VLC_FOURCC('Y','U','Y','2'):
390             break;
391             
392             /* chromas in which scaling is done in our blending 
393                routine and thus we don't do it here: */
394           case VLC_FOURCC('R','V','1','6'):
395           case VLC_FOURCC('R','V','2','4'):
396           case VLC_FOURCC('R','V','3','2'):
397           case VLC_FOURCC('R','G','B','2'):
398             return;
399             break;
400             
401           default:
402             msg_Err( p_vout, "unknown chroma %x", 
403                      p_vout->output.i_chroma );
404             return;
405             break;
406           }
407         /* We get here only for scaled chromas. */
408         vlc_reduce( &i_aspect_x, &i_aspect_y, p_vout->render.i_aspect,
409                     VOUT_ASPECT_FACTOR, 0 );
410       } else {
411         /* User knows best? */
412         vlc_reduce( &i_aspect_x, &i_aspect_y, p_vout->render.i_aspect,
413                     VOUT_ASPECT_FACTOR, 0 );
414       }
415       VCDSubScaleX( p_dec, p_spu, i_aspect_x, i_aspect_y );
416     }
417   }
418 }
419
420
421 /**
422  * DestroySPU: subpicture destructor
423  */
424 void VCDSubDestroySPU( subpicture_t *p_spu )
425 {
426     if( p_spu->p_sys->p_input )
427     {
428         /* Detach from our input thread */
429         vlc_object_release( p_spu->p_sys->p_input );
430     }
431
432     vlc_mutex_destroy( &p_spu->p_sys->lock );
433     free( p_spu->p_sys );
434 }
435
436 /*****************************************************************************
437   This callback is called from the input thread when we need cropping
438  *****************************************************************************/
439 int VCDSubCropCallback( vlc_object_t *p_object, char const *psz_var,
440                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
441 {
442     VCDSubUpdateSPU( (subpicture_t *)p_data, p_object );
443
444     return VLC_SUCCESS;
445 }
446
447
448 /*****************************************************************************
449   update subpicture settings
450  *****************************************************************************
451   This function is called from CropCallback and at initialization time, to
452   retrieve crop information from the input.
453  *****************************************************************************/
454 void VCDSubUpdateSPU( subpicture_t *p_spu, vlc_object_t *p_object )
455 {
456     vlc_value_t val;
457
458     p_spu->p_sys->b_crop = val.b_bool;
459     if( !p_spu->p_sys->b_crop )
460     {
461         return;
462     }
463
464     if ( VLC_SUCCESS == var_Get( p_object, "x-start", &val ) )
465       p_spu->p_sys->i_x_start = val.i_int;
466     if ( VLC_SUCCESS == var_Get( p_object, "y-start", &val ) )
467       p_spu->p_sys->i_y_start = val.i_int;
468     if ( VLC_SUCCESS == var_Get( p_object, "x-end", &val ) )
469       p_spu->p_sys->i_x_end = val.i_int;
470     if ( VLC_SUCCESS == var_Get( p_object, "y-end", &val ) )
471       p_spu->p_sys->i_y_end = val.i_int;
472
473 }
474
475 /* 
476    Dump an a subtitle image to standard output - for debugging.
477  */
478 void VCDSubDumpImage( uint8_t *p_image, uint32_t i_height, uint32_t i_width )
479 {
480   uint8_t *p = p_image;
481   unsigned int i_row;    /* scanline row number */
482   unsigned int i_column; /* scanline column number */
483
484   printf("-------------------------------------\n++");
485   for ( i_row=0; i_row < i_height; i_row ++ ) {
486     for ( i_column=0; i_column<i_width; i_column++ ) {
487       printf("%1d", *p++ & 0x03);
488     }
489     printf("\n++");
490   }
491   printf("\n-------------------------------------\n");
492 }
493
494 #ifdef HAVE_LIBPNG
495
496 #define PALETTE_SIZE  4
497 /* Note the below assumes the above is a power of 2 */
498 #define PALETTE_SIZE_MASK (PALETTE_SIZE-1)
499
500 /* 
501    Dump an a subtitle image to a Portable Network Graphics (PNG) file.
502    All we do here is convert YUV palette entries to RGB, expand
503    the image into a linear RGB pixel array, and call the routine
504    that does the PNG writing.
505  */
506
507 void 
508 VCDSubDumpPNG( uint8_t *p_image, decoder_t *p_dec,
509                uint32_t i_height, uint32_t i_width, const char *filename,
510                png_text *text_ptr, int i_text_count )
511 {
512   decoder_sys_t *p_sys = p_dec->p_sys;
513   uint8_t *p = p_image;
514   uint8_t *image_data = malloc(RGB_SIZE * i_height * i_width );
515   uint8_t *q = image_data;
516   unsigned int i_row;    /* scanline row number */
517   unsigned int i_column; /* scanline column number */
518   uint8_t rgb_palette[PALETTE_SIZE * RGB_SIZE];
519   int i;
520
521   dbg_print( (DECODE_DBG_CALL), "%s", filename);
522   
523   if (NULL == image_data) return;
524
525   /* Convert palette YUV into RGB. */
526   for (i=0; i<PALETTE_SIZE; i++) {
527     ogt_yuvt_t *p_yuv     = &(p_sys->p_palette[i]);
528     uint8_t   *p_rgb_out  = &(rgb_palette[i*RGB_SIZE]);
529     yuv2rgb( p_yuv, p_rgb_out );
530   }
531   
532   /* Convert palette entries into linear RGB array. */
533   for ( i_row=0; i_row < i_height; i_row ++ ) {
534     for ( i_column=0; i_column<i_width; i_column++ ) {
535       uint8_t *p_rgb = &rgb_palette[ ((*p)&PALETTE_SIZE_MASK)*RGB_SIZE ];
536       *q++ = p_rgb[0];
537       *q++ = p_rgb[1];
538       *q++ = p_rgb[2];
539       p++;
540     }
541   }
542   
543   write_png( filename, i_height, i_width, image_data, text_ptr, i_text_count );
544   free(image_data);
545 }
546 #endif /*HAVE_LIBPNG*/
547
548 \f
549 /* 
550  * Local variables:
551  *  c-file-style: "gnu"
552  *  tab-width: 8
553  *  indent-tabs-mode: nil
554  * End:
555  */