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