render.c: RV16 now works great - uses all colors, but no transparancy.
[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.7 2004/01/16 04:14:54 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 void
183 VCDInlinePalette ( /*inout*/ uint8_t *p_dest, decoder_sys_t *p_sys,
184                    unsigned int i_height, unsigned int i_width ) 
185 {
186   int n = (i_height * i_width) - 1;
187   uint8_t    *p_from = p_dest;
188   ogt_yuvt_t *p_to   = (ogt_yuvt_t *) p_dest;
189   
190   for ( ; n >= 0 ; n-- ) {
191     p_to[n] = p_sys->p_palette[p_from[n]];
192     /*p_to[n] = p_sys->p_palette[p_from[3]];*/
193   }
194 }
195
196 /**
197    Check to see if user has overridden subtitle aspect ratio. 
198    0 is returned for no override which means just counteract any
199    scaling effects.
200 */
201 unsigned int 
202 VCDSubGetAROverride(vlc_object_t * p_input, vout_thread_t *p_vout)
203 {
204   char *psz_string = config_GetPsz( p_input, "sub-aspect-ratio" );
205
206   /* Check whether the user tried to override aspect ratio */
207   if( !psz_string ) return 0;
208
209   {
210     unsigned int i_new_aspect = 0;
211     char *psz_parser = strchr( psz_string, ':' );
212     
213     if( psz_parser )
214       {
215         *psz_parser++ = '\0';
216         i_new_aspect = atoi( psz_string ) * VOUT_ASPECT_FACTOR
217           / atoi( psz_parser );
218       }
219     else
220       {
221         i_new_aspect = p_vout->output.i_width * VOUT_ASPECT_FACTOR
222           * atof( psz_string )
223           / p_vout->output.i_height;
224       }
225     
226     return i_new_aspect;
227   }
228 }
229
230
231 /**
232    Scales down (reduces size) of p_dest in the x direction as 
233    determined through aspect ratio x_scale by y_scale. Scaling
234    is done in place. p_spu->i_width, is updated to new width
235
236    The aspect ratio is assumed to be between 1/2 and 1.
237
238    Note: the scaling truncates the new width rather than rounds it.
239    Perhaps something one might want to address.
240 */
241 void
242 VCDSubScaleX( decoder_t *p_dec, subpicture_t *p_spu, 
243               unsigned int i_scale_x, unsigned int i_scale_y )
244 {
245   int i_row, i_col;
246
247   decoder_sys_t *p_sys = p_dec->p_sys;
248   uint8_t *p_src1 = p_spu->p_sys->p_data;
249   uint8_t *p_src2 = p_src1 + PIXEL_SIZE;
250   uint8_t *p_dst  = p_src1;
251   unsigned int i_new_width = (p_spu->i_width * i_scale_x) / i_scale_y ;
252   unsigned int i_used=0;  /* Number of bytes used up in p_src1. */
253
254   dbg_print( (DECODE_DBG_CALL|DECODE_DBG_TRANSFORM) , 
255              "aspect ratio %i:%i, Old width: %d, new width: %d", 
256              i_scale_x, i_scale_y, p_spu->i_width, i_new_width);
257
258   if (! (i_scale_x < i_scale_y && i_scale_y < i_scale_x+i_scale_x) )
259     {
260       msg_Warn( p_dec, "Need x < y < 2x. x: %i, y: %i", i_scale_x, i_scale_y );
261       return;
262     }
263   
264   for ( i_row=0; i_row <= p_spu->i_height - 1; i_row++ ) {
265
266     if (i_used != 0) {
267       /* Discard the remaining piece of the column of the previous line*/
268       i_used=0;
269       p_src1 = p_src2;
270       p_src2 += PIXEL_SIZE;
271     }
272     
273     for ( i_col=0; i_col <= p_spu->i_width - 2; i_col++ ) {
274       unsigned int i;
275       unsigned int w1= i_scale_x - i_used;
276       unsigned int w2;
277       
278       if ( i_scale_y - w1 <= i_scale_x ) {
279         /* Average spans 2 pixels. */
280         w2 = i_scale_y - w1;
281
282         for (i = 0; i < PIXEL_SIZE; i++ ) {
283           *p_dst = ( (*p_src1 * w1) + (*p_src2 * w2) ) / i_scale_y;
284           p_src1++; p_src2++; p_dst++;
285         }
286       } else {
287         /* Average spans 3 pixels. */
288         unsigned int w0 = w1;
289         unsigned int w1 = i_scale_x;
290         uint8_t *p_src0 = p_src1;
291         w2 = i_scale_y - w0 - w1;
292         p_src1 = p_src2;
293         p_src2 += PIXEL_SIZE;
294         
295         for (i = 0; i < PIXEL_SIZE; i++ ) {
296           *p_dst = ( (*p_src0 * w0) + (*p_src1 * w1) + (*p_src2 * w2) ) 
297                      / i_scale_y;
298           p_src0++; p_src1++; p_src2++; p_dst++;
299         }
300         i_col++;
301       }
302
303       i_used = w2;
304
305       if (i_scale_x == i_used) {
306         /* End of last pixel was end of p_src2. */
307         p_src1 = p_src2;
308         p_src2 += PIXEL_SIZE;
309         i_col++;
310         i_used = 0;
311       }
312     }
313   }
314   p_spu->i_width = i_new_width;
315
316   if ( p_sys && p_sys->i_debug & DECODE_DBG_TRANSFORM )
317   { 
318     ogt_yuvt_t *p_source = (ogt_yuvt_t *) p_spu->p_sys->p_data;
319     for ( i_row=0; i_row < p_spu->i_height; i_row++ ) {
320       for ( i_col=0; i_col < p_spu->i_width; i_col++ ) {
321         printf("%1x", p_source->s.t);
322         p_source++;
323       }
324       printf("\n");
325     }
326   }
327
328 }
329
330 /* The video may be scaled. However subtitle bitmaps assume an 1:1
331    aspect ratio. So unless the user has specified otherwise, we
332    need to scale to compensate for or undo the effects of video
333    output scaling.
334    
335    Perhaps this should go in the Render routine? The advantage would
336    be that it will deal with a dynamically changing aspect ratio.
337    The downside is having to scale many times for each render call.
338 */
339
340 void 
341 VCDSubHandleScaling( subpicture_t *p_spu, decoder_t *p_dec )
342 {
343   vlc_object_t * p_input = p_spu->p_sys->p_input;
344   vout_thread_t *p_vout = vlc_object_find( p_input, VLC_OBJECT_VOUT, 
345                                            FIND_CHILD );
346   unsigned int i_aspect_x, i_aspect_y;
347   if (p_vout) {
348     /* Check for user-configuration override. */
349     unsigned int i_new_aspect = VCDSubGetAROverride( p_input, p_vout );
350
351     if (i_new_aspect == VOUT_ASPECT_FACTOR) {
352       /* For scaling 1:1, nothing needs to be done. Note this means
353          subtitles will get scaled the same way the video does.
354       */
355       ;
356     } else {
357       if (0 == i_new_aspect) {
358         /* Counteract the effects of background video scaling when
359            there is scaling. That's why x and y are reversed from
360            the else branch in the call below.
361         */
362         switch( p_vout->output.i_chroma )
363           {
364             /* chromas which are not scaled: */
365           case VLC_FOURCC('I','4','2','0'):
366           case VLC_FOURCC('I','Y','U','V'):
367           case VLC_FOURCC('Y','V','1','2'):
368           case VLC_FOURCC('Y','U','Y','2'):
369             break;
370             
371             /* chromas which are scaled: */
372           case VLC_FOURCC('R','V','1','6'):
373           case VLC_FOURCC('R','V','2','4'):
374           case VLC_FOURCC('R','V','3','2'):
375           case VLC_FOURCC('R','G','B','2'):
376             return;
377             break;
378             
379           default:
380             msg_Err( p_vout, "unknown chroma %x", 
381                      p_vout->output.i_chroma );
382             return;
383             break;
384           }
385         /* We get here only for scaled chromas. */
386         vout_AspectRatio( p_vout->render.i_aspect, &i_aspect_y, 
387                           &i_aspect_x );
388       } else {
389         /* User knows best? */
390         vout_AspectRatio( i_new_aspect, &i_aspect_x, &i_aspect_y );
391       }
392       VCDSubScaleX( p_dec, p_spu, i_aspect_x, i_aspect_y );
393     }
394   }
395 }
396
397
398 /**
399  * DestroySPU: subpicture destructor
400  */
401 void VCDSubDestroySPU( subpicture_t *p_spu )
402 {
403     if( p_spu->p_sys->p_input )
404     {
405         /* Detach from our input thread */
406         vlc_object_release( p_spu->p_sys->p_input );
407     }
408
409     vlc_mutex_destroy( &p_spu->p_sys->lock );
410     free( p_spu->p_sys );
411 }
412
413 /*****************************************************************************
414   This callback is called from the input thread when we need cropping
415  *****************************************************************************/
416 int VCDSubCropCallback( vlc_object_t *p_object, char const *psz_var,
417                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
418 {
419     VCDSubUpdateSPU( (subpicture_t *)p_data, p_object );
420
421     return VLC_SUCCESS;
422 }
423
424
425 /*****************************************************************************
426   update subpicture settings
427  *****************************************************************************
428   This function is called from CropCallback and at initialization time, to
429   retrieve crop information from the input.
430  *****************************************************************************/
431 void VCDSubUpdateSPU( subpicture_t *p_spu, vlc_object_t *p_object )
432 {
433     vlc_value_t val;
434
435     p_spu->p_sys->b_crop = val.b_bool;
436     if( !p_spu->p_sys->b_crop )
437     {
438         return;
439     }
440
441     if ( VLC_SUCCESS == var_Get( p_object, "x-start", &val ) )
442       p_spu->p_sys->i_x_start = val.i_int;
443     if ( VLC_SUCCESS == var_Get( p_object, "y-start", &val ) )
444       p_spu->p_sys->i_y_start = val.i_int;
445     if ( VLC_SUCCESS == var_Get( p_object, "x-end", &val ) )
446       p_spu->p_sys->i_x_end = val.i_int;
447     if ( VLC_SUCCESS == var_Get( p_object, "y-end", &val ) )
448       p_spu->p_sys->i_y_end = val.i_int;
449
450 }
451
452 /* 
453    Dump an a subtitle image to standard output - for debugging.
454  */
455 void VCDSubDumpImage( uint8_t *p_image, uint32_t i_height, uint32_t i_width )
456 {
457   uint8_t *p = p_image;
458   unsigned int i_row;    /* scanline row number */
459   unsigned int i_column; /* scanline column number */
460
461   printf("-------------------------------------\n++");
462   for ( i_row=0; i_row < i_height; i_row ++ ) {
463     for ( i_column=0; i_column<i_width; i_column++ ) {
464       printf("%1d", *p++ & 0x03);
465     }
466     printf("\n++");
467   }
468   printf("\n-------------------------------------\n");
469 }
470
471 /***************************************************
472    Color conversion from
473     http://www.inforamp.net/~poynton/notes/colour_and_gamma/ColorFAQ.html#RTFToC30
474     http://people.ee.ethz.ch/~buc/brechbuehler/mirror/color/ColorFAQ.html
475  
476     Thanks to Billy Biggs <vektor@dumbterm.net> for the pointer and
477     the following conversion.
478  
479     R' = [ 1.1644         0    1.5960 ]   ([ Y' ]   [  16 ])
480     G' = [ 1.1644   -0.3918   -0.8130 ] * ([ Cb ] - [ 128 ])
481     B' = [ 1.1644    2.0172         0 ]   ([ Cr ]   [ 128 ])
482
483   See also vlc/modules/video_chroma/i420_rgb.h and
484   vlc/modules/video_chroma/i420_rgb_c.h for a way to do this in a way
485   more optimized for integer arithmetic. Would be nice to merge the
486   two routines.
487  
488 ***************************************************/
489
490 #ifdef HAVE_LIBPNG
491
492 #define BYTES_PER_RGB 3
493 #define PALETTE_SIZE  4
494 /* Note the below assumes the above is a power of 2 */
495 #define PALETTE_SIZE_MASK (PALETTE_SIZE-1)
496
497 /* 
498    Dump an a subtitle image to a Portable Network Graphics (PNG) file.
499    All we do here is convert YUV palette entries to RGB, expand
500    the image into a linear RGB pixel array, and call the routine
501    that does the PNG writing.
502  */
503
504 void 
505 VCDSubDumpPNG( uint8_t *p_image, decoder_t *p_dec,
506                uint32_t i_height, uint32_t i_width, const char *filename,
507                png_text *text_ptr, int i_text_count )
508 {
509   decoder_sys_t *p_sys = p_dec->p_sys;
510   uint8_t *p = p_image;
511   uint8_t *image_data = malloc(BYTES_PER_RGB * i_height * i_width );
512   uint8_t *q = image_data;
513   unsigned int i_row;    /* scanline row number */
514   unsigned int i_column; /* scanline column number */
515   uint8_t rgb_palette[PALETTE_SIZE * BYTES_PER_RGB];
516   int i;
517
518   dbg_print( (DECODE_DBG_CALL), "%s", filename);
519   
520   if (NULL == image_data) return;
521
522   /* Convert palette YUV into RGB. */
523   for (i=0; i<PALETTE_SIZE; i++) {
524     ogt_yuvt_t *p_yuv     = &(p_sys->p_palette[i]);
525     uint8_t   *p_rgb_out  = &(rgb_palette[i*BYTES_PER_RGB]);
526     yuv2rgb( p_yuv, p_rgb_out );
527   }
528   
529   /* Convert palette entries into linear RGB array. */
530   for ( i_row=0; i_row < i_height; i_row ++ ) {
531     for ( i_column=0; i_column<i_width; i_column++ ) {
532       uint8_t *p_rgb = &rgb_palette[ ((*p)&PALETTE_SIZE_MASK)*BYTES_PER_RGB ];
533       *q++ = p_rgb[0];
534       *q++ = p_rgb[1];
535       *q++ = p_rgb[2];
536       p++;
537     }
538   }
539   
540   write_png( filename, i_height, i_width, image_data, text_ptr, i_text_count );
541   free(image_data);
542 }
543 #endif /*HAVE_LIBPNG*/
544
545 \f
546 /* 
547  * Local variables:
548  *  c-file-style: "gnu"
549  *  tab-width: 8
550  *  indent-tabs-mode: nil
551  * End:
552  */