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