]> git.sesse.net Git - vlc/blob - modules/codec/png.c
80e85b0f1c6621c231d345308ac90b638cca5bad
[vlc] / modules / codec / png.c
1 /*****************************************************************************
2  * png.c: png decoder module making use of libpng.
3  *****************************************************************************
4  * Copyright (C) 1999-2001 VideoLAN
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <vlc/vlc.h>
28 #include <vlc/decoder.h>
29
30 #include <png.h>
31
32 /*****************************************************************************
33  * decoder_sys_t : png decoder descriptor
34  *****************************************************************************/
35 struct decoder_sys_t
36 {
37     vlc_bool_t b_error;
38 };
39
40 /*****************************************************************************
41  * Local prototypes
42  *****************************************************************************/
43 static int  OpenDecoder   ( vlc_object_t * );
44 static void CloseDecoder  ( vlc_object_t * );
45
46 static picture_t *DecodeBlock  ( decoder_t *, block_t ** );
47
48 static int  OpenEncoder   ( vlc_object_t * );
49 static void CloseEncoder  ( vlc_object_t * );
50 static block_t *Encode( encoder_t *p_enc, picture_t *p_pic );
51
52 /*****************************************************************************
53  * Module descriptor
54  *****************************************************************************/
55 vlc_module_begin();
56     set_category( CAT_INPUT );
57     set_subcategory( SUBCAT_INPUT_VCODEC );
58     set_shortname( _("PNG" ) );
59     set_description( _("PNG image decoder") );
60     set_capability( "decoder", 1000 );
61     set_callbacks( OpenDecoder, CloseDecoder );
62     add_shortcut( "png" );
63
64     add_submodule();
65     set_description( _( "PNG image encoder" ) );
66     set_capability( "encoder", 100 );
67     set_callbacks( OpenEncoder, CloseEncoder );
68     add_shortcut( "png" );
69 vlc_module_end();
70
71 /*****************************************************************************
72  * OpenDecoder: probe the decoder and return score
73  *****************************************************************************/
74 static int OpenDecoder( vlc_object_t *p_this )
75 {
76     decoder_t *p_dec = (decoder_t*)p_this;
77     decoder_sys_t *p_sys;
78
79     if( p_dec->fmt_in.i_codec != VLC_FOURCC('p','n','g',' ') )
80     {
81         return VLC_EGENERIC;
82     }
83
84     /* Allocate the memory needed to store the decoder's structure */
85     if( ( p_dec->p_sys = p_sys =
86           (decoder_sys_t *)malloc(sizeof(decoder_sys_t)) ) == NULL )
87     {
88         msg_Err( p_dec, "out of memory" );
89         return VLC_EGENERIC;
90     }
91
92     /* Set output properties */
93     p_dec->fmt_out.i_cat = VIDEO_ES;
94     p_dec->fmt_out.i_codec = VLC_FOURCC('R','V','3','2');
95
96     /* Set callbacks */
97     p_dec->pf_decode_video = DecodeBlock;
98
99     return VLC_SUCCESS;
100 }
101
102 static void user_read( png_structp p_png, png_bytep data, png_size_t i_length )
103 {
104     block_t *p_block = (block_t *)png_get_io_ptr( p_png );
105     png_size_t i_read = __MIN( p_block->i_buffer, (int)i_length );
106     memcpy( data, p_block->p_buffer, i_length );
107     p_block->p_buffer += i_length;
108     p_block->i_buffer -= i_length;
109
110     if( i_length != i_read ) png_error( p_png, "not enough data" );
111 }
112
113 static void user_error( png_structp p_png, png_const_charp error_msg )
114 {
115     decoder_t *p_dec = (decoder_t *)png_get_error_ptr( p_png );
116     p_dec->p_sys->b_error = VLC_TRUE;
117     msg_Err( p_dec, error_msg );
118 }
119
120 static void user_warning( png_structp p_png, png_const_charp warning_msg )
121 {
122     decoder_t *p_dec = (decoder_t *)png_get_error_ptr( p_png );
123     msg_Warn( p_dec, warning_msg );
124 }
125
126 /****************************************************************************
127  * DecodeBlock: the whole thing
128  ****************************************************************************
129  * This function must be fed with a complete compressed frame.
130  ****************************************************************************/
131 static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block )
132 {
133     decoder_sys_t *p_sys = p_dec->p_sys;
134     block_t *p_block;
135     picture_t *p_pic = 0;
136
137     png_uint_32 i_width, i_height;
138     int i_color_type, i_interlace_type, i_compression_type, i_filter_type;
139     int i_bit_depth, i;
140
141     png_structp p_png;
142     png_infop p_info, p_end_info;
143     png_bytep *p_row_pointers = NULL;
144
145     if( !pp_block || !*pp_block ) return NULL;
146
147     p_block = *pp_block;
148     p_sys->b_error = VLC_FALSE;
149
150     p_png = png_create_read_struct( PNG_LIBPNG_VER_STRING, 0, 0, 0 );
151     p_info = png_create_info_struct( p_png );
152     p_end_info = png_create_info_struct( p_png );
153
154     png_set_read_fn( p_png, (void *)p_block, user_read );
155     png_set_error_fn( p_png, (void *)p_dec, user_error, user_warning );
156
157     png_read_info( p_png, p_info );
158     if( p_sys->b_error ) goto error;
159
160     png_get_IHDR( p_png, p_info, &i_width, &i_height,
161                   &i_bit_depth, &i_color_type, &i_interlace_type,
162                   &i_compression_type, &i_filter_type);
163     if( p_sys->b_error ) goto error;
164
165     /* Set output properties */
166     p_dec->fmt_out.i_codec = VLC_FOURCC('R','V','3','2');
167     p_dec->fmt_out.video.i_width = i_width;
168     p_dec->fmt_out.video.i_height = i_height;
169     p_dec->fmt_out.video.i_aspect = VOUT_ASPECT_FACTOR * i_width / i_height;
170
171     if( i_color_type == PNG_COLOR_TYPE_PALETTE )
172         png_set_palette_to_rgb( p_png );
173
174     if( i_color_type == PNG_COLOR_TYPE_GRAY ||
175         i_color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
176           png_set_gray_to_rgb( p_png );
177
178     /* Strip to 8 bits per channel */
179     if( i_bit_depth == 16 ) png_set_strip_16( p_png );
180
181     if( png_get_valid( p_png, p_info, PNG_INFO_tRNS ) )
182     {
183         png_set_tRNS_to_alpha( p_png );
184     }
185     else if( !(i_color_type & PNG_COLOR_MASK_ALPHA) )
186     {
187         p_dec->fmt_out.i_codec = VLC_FOURCC('R','V','2','4');
188     }
189     if( i_color_type & PNG_COLOR_MASK_COLOR &&
190         p_dec->fmt_out.i_codec != VLC_FOURCC('R','V','2','4') )
191     {
192         /* Invert colors */
193         png_set_bgr( p_png );
194     }
195
196     /* Get a new picture */
197     p_pic = p_dec->pf_vout_buffer_new( p_dec );
198     if( !p_pic ) goto error;
199
200     /* Decode picture */
201     p_row_pointers = malloc( sizeof(png_bytep) * i_height );
202     for( i = 0; i < (int)i_height; i++ )
203         p_row_pointers[i] = p_pic->p->p_pixels + p_pic->p->i_pitch * i;
204
205     png_read_image( p_png, p_row_pointers );
206     if( p_sys->b_error ) goto error;
207     png_read_end( p_png, p_end_info );
208     if( p_sys->b_error ) goto error;
209
210     png_destroy_read_struct( &p_png, &p_info, &p_end_info );
211     free( p_row_pointers );
212
213     block_Release( p_block ); *pp_block = NULL;
214     return p_pic;
215
216  error:
217
218     if( p_row_pointers ) free( p_row_pointers );
219     png_destroy_read_struct( &p_png, &p_info, &p_end_info );
220     block_Release( p_block ); *pp_block = NULL;
221     return NULL;
222 }
223
224 /*****************************************************************************
225  * CloseDecoder: png decoder destruction
226  *****************************************************************************/
227 static void CloseDecoder( vlc_object_t *p_this )
228 {
229     decoder_t *p_dec = (decoder_t *)p_this;
230     decoder_sys_t *p_sys = p_dec->p_sys;
231
232     free( p_sys );
233 }
234
235 /*****************************************************************************
236  * PNG Encoder
237  *****************************************************************************/
238 struct encoder_sys_t
239 {
240     block_t *p_chain;
241     mtime_t date;
242     vlc_bool_t b_error;
243 };
244
245
246 static int OpenEncoder( vlc_object_t *p_this )
247 {
248     encoder_t *p_enc = (encoder_t *)p_this;
249     encoder_sys_t *p_sys = p_enc->p_sys;
250
251     if( p_enc->fmt_out.i_codec != VLC_FOURCC( 'p', 'n', 'g', ' ' ) &&
252         !p_enc->b_force )
253     {
254         return VLC_EGENERIC;
255     }
256
257     /* Allocate the memory needed to store the encoder structure */
258     if( ( p_sys = (encoder_sys_t *)malloc(sizeof(encoder_sys_t)) ) == NULL )
259     {
260         msg_Err( p_enc, "out of memory" );
261         return VLC_EGENERIC;
262     }
263
264     p_enc->p_sys = p_sys;
265     p_enc->p_sys->p_chain = NULL;
266
267     p_enc->pf_encode_video = Encode;
268
269     return VLC_SUCCESS;
270 }
271
272 static void CloseEncoder( vlc_object_t *p_this )
273 {
274 }
275
276 static void user_write( png_structp p_png, png_bytep data, png_size_t i_length )
277 {
278     encoder_t *p_enc = (encoder_t *)png_get_io_ptr( p_png );
279
280     block_t *p_block = block_New( p_enc, i_length );
281
282     memcpy( p_block->p_buffer, data, i_length );
283     p_block->i_dts = p_block->i_pts =  p_enc->p_sys->date;
284     block_ChainAppend( &p_enc->p_sys->p_chain, p_block );
285 }
286
287 static void user_flush( png_structp p_png )
288 {
289 }
290
291 static void user_write_error( png_structp p_png, png_const_charp error_msg )
292 {
293     encoder_t *p_enc = (encoder_t *)png_get_error_ptr( p_png );
294     p_enc->p_sys->b_error = VLC_TRUE;
295     msg_Err( p_enc, error_msg );
296 }
297
298 static void user_write_warning( png_structp p_png, png_const_charp warning_msg )
299 {
300     encoder_t *p_enc = (encoder_t *)png_get_error_ptr( p_png );
301     msg_Warn( p_enc, warning_msg );
302 }
303
304
305 static block_t *Encode( encoder_t *p_enc, picture_t *p_pic )
306 {
307     png_structp p_png;
308     png_infop p_info;
309     png_bytep *p_row_pointers = NULL;
310     int i;
311     block_t *p_gather = NULL;
312
313     p_enc->p_sys->date = p_pic->date;
314
315     p_png =  png_create_write_struct( PNG_LIBPNG_VER_STRING, 0, 0, 0 );
316     p_info = png_create_info_struct( p_png );
317
318     png_set_write_fn( p_png, (void *)p_enc, user_write, user_flush );
319     png_set_error_fn( p_png, (void *)p_enc, user_write_error,
320                                             user_write_warning );
321
322     png_set_IHDR( p_png, p_info,
323                   p_pic->format.i_width,
324                   p_pic->format.i_height,
325                   8, PNG_COLOR_TYPE_RGB,
326                   PNG_INTERLACE_NONE,
327                   PNG_COMPRESSION_TYPE_DEFAULT,
328                   PNG_FILTER_TYPE_DEFAULT );
329
330     p_row_pointers = malloc( sizeof(png_bytep) * p_pic->format.i_height );
331     for( i = 0; i < (int)p_pic->format.i_height; i++ )
332         p_row_pointers[i] = p_pic->p->p_pixels + p_pic->p->i_pitch * i;
333
334     png_write_info( p_png, p_info );
335     png_write_image( p_png, p_row_pointers );
336     png_write_end( p_png, p_info );
337
338     png_destroy_write_struct( &p_png, &p_info );
339
340     if( p_enc->p_sys->p_chain )
341     {
342         p_gather = block_ChainGather( p_enc->p_sys->p_chain );
343     }
344     p_enc->p_sys->p_chain = NULL;
345
346     free( p_row_pointers );
347
348     return p_gather;
349 }