Support unrar64.dll
[xy_vsfilter.git] / src / apps / mplayerc / pngdib.c
blob187c8aa324917f7247a5efc7fce31a2bee79dd45
1 // pngdib.c
2 //
3 // PNGDIB - a mini PNG<->DIB (BMP) image conversion library for Win32
4 // By Jason Summers
5 // This software may be used without restriction.
6 //
8 #include <windows.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <assert.h>
12 #include <malloc.h>
13 #include <math.h>
15 #include "..\..\libpng\png.h"
17 #define PNGDIB_INTERNALS
18 #include "pngdib.h"
21 #define PNGDIB_SRC_VERSION 30001
22 #define PNGDIB_SRC_VERSION_STRING _T("3.0.1")
25 #if PNGDIB_SRC_VERSION != PNGDIB_HEADER_VERSION
26 #error Wrong PNGDIB header file version
27 #endif
29 #if (PNG_LIBPNG_VER<10202) || \
30 (PNG_LIBPNG_VER==10202 && PNG_LIBPNG_BUILD_TYPE<2) || \
31 (PNG_LIBPNG_VER==10202 && PNG_LIBPNG_BUILD_TYPE==2 && PNG_LIBPNG_VER_BUILD<5)
32 #error libpng 1.2.2b5 or higher is recommended
33 /* You can comment out the previous line if you aren't using gamma
34 * correction, or don't care about a few obscure gamma correction
35 * problems that exist in earlier versions of libpng. */
36 #endif
39 // This is basically a Windows-only utility with a simple-as-possible
40 // interface, so I'm not too concerned about allowing a
41 // user-configurable screen gamma.
42 //static const double screen_gamma = 2.2;
44 #define MAX_ERRMSGLEN 100
46 struct errstruct {
47 jmp_buf *jbufp;
48 TCHAR *errmsg;
51 static void pngd_get_error_message(int rv,TCHAR *e)
53 switch(rv) {
54 case PNGD_E_ERROR: lstrcpy(e,_T("Unknown error")); break;
55 case PNGD_E_VERSION: lstrcpy(e,_T("Incompatible library version")); break;
56 case PNGD_E_NOMEM: lstrcpy(e,_T("Unable to allocate memory")); break;
57 case PNGD_E_UNSUPP: lstrcpy(e,_T("Invalid or unsupported image")); break;
58 case PNGD_E_LIBPNG: lstrcpy(e,_T("libpng reported an error")); break;
59 case PNGD_E_BADBMP: lstrcpy(e,_T("Invalid BMP image")); break;
60 case PNGD_E_BADPNG: lstrcpy(e,_T("Invalid PNG image")); break;
61 case PNGD_E_READ: lstrcpy(e,_T("Unable to read file")); break;
62 case PNGD_E_WRITE: lstrcpy(e,_T("Unable to write file")); break;
66 static unsigned char* uncompress_dib(LPBITMAPINFO lpbmi1, int infosize, void *lpbits1)
68 LPBITMAPINFOHEADER lpdib2;
69 unsigned char *lpbits2;
70 void *whatever;
71 int linesize, bitssize;
72 HBITMAP hb;
73 HDC hdc;
74 HGDIOBJ rvgdi;
75 int rvi;
76 int width,height;
77 LPBITMAPINFOHEADER lpdib1;
79 lpdib1=(LPBITMAPINFOHEADER)lpbmi1;
80 width=lpdib1->biWidth;
81 height=lpdib1->biHeight;
83 linesize= (((width * lpdib1->biBitCount)+31)/32)*4;
84 bitssize= linesize*height;
86 lpdib2= (LPBITMAPINFOHEADER)malloc(infosize);
87 if(!lpdib2) return NULL;
89 // create a header for the new uncompressed DIB
90 CopyMemory((void*)lpdib2,(void*)lpdib1,infosize);
91 lpdib2->biCompression=BI_RGB;
92 lpdib2->biSizeImage=0;
94 lpbits2= (unsigned char*)malloc(bitssize);
95 if(!lpbits2) { free((void*)lpdib2); return NULL; }
98 // Windows bitmap handling functions are not exactly convenient,
99 // especially when trying to deal with DIBs. Every function wants
100 // to convert them into DDBs. We have to play stupid games and
101 // convert back and forth. This probably uses too much memory,
102 // and I'm not 100% sure it is exactly correct, but it seems to
103 // work for me.
105 hb=CreateDIBSection(NULL,(LPBITMAPINFO)lpdib2,DIB_RGB_COLORS,&whatever,NULL,0);
107 hdc=CreateCompatibleDC(NULL);
108 rvgdi=SelectObject(hdc,hb);
109 //SetStretchBltMode(hdc,COLORONCOLOR);
110 rvi=StretchDIBits(hdc,
111 0,0,width,height,
112 0,0,width,height,
113 lpbits1, (LPBITMAPINFO)lpdib1,
114 DIB_RGB_COLORS,SRCCOPY);
115 rvi=GetDIBits(hdc,hb,0,height, (LPVOID)lpbits2,
116 (LPBITMAPINFO)lpdib2,DIB_RGB_COLORS);
118 DeleteDC(hdc);
119 DeleteObject(hb);
120 free((void*)lpdib2);
122 return lpbits2;
126 static void my_png_error_fn(png_structp png_ptr, const char *err_msg)
128 struct errstruct *errinfop;
129 jmp_buf *j;
131 errinfop = (struct errstruct *)png_get_error_ptr(png_ptr);
132 j = errinfop->jbufp;
134 #ifdef _UNICODE
135 _snwprintf(errinfop->errmsg,MAX_ERRMSGLEN,_T("[libpng] %S"),err_msg);
136 #else
137 _snprintf(errinfop->errmsg,MAX_ERRMSGLEN,"[libpng] %s",err_msg);
138 #endif
140 errinfop->errmsg[MAX_ERRMSGLEN-1]='\0';
141 longjmp(*j, -1);
145 static void my_png_warning_fn(png_structp png_ptr, const char *warn_msg)
147 return;
150 // A callback function used when reading memory-mapped PNG files.
151 static void my_png_read_fn(png_structp png_ptr,
152 png_bytep data, png_size_t length)
154 struct p2d_struct *p2d;
156 p2d = (struct p2d_struct*)png_get_io_ptr(png_ptr);
158 if(p2d->input_memblk_size>0) {
159 if((int)length > (p2d->input_memblk_size - p2d->input_memblk_curpos)) {
160 png_error(png_ptr, "read error: unexpected end of file");
161 return;
165 CopyMemory((void*)data,(void*)&p2d->input_memblk[p2d->input_memblk_curpos],length);
166 p2d->input_memblk_curpos+=length;
170 // This function should perform identically to libpng's gamma correction.
171 // I'd prefer to have libpng do all gamma correction itself,
172 // but I can't figure out how to do that efficiently.
173 static void gamma_correct(double screen_gamma,double file_gamma,
174 unsigned char *red, unsigned char *green, unsigned char *blue)
176 double g;
178 #ifndef PNG_GAMMA_THRESHOLD
179 # define PNG_GAMMA_THRESHOLD 0.05
180 #endif
182 if(fabs(screen_gamma*file_gamma-1.0)<=PNG_GAMMA_THRESHOLD) return;
184 if (screen_gamma>0.000001)
185 g=1.0/(file_gamma*screen_gamma);
186 else
187 g=1.0;
189 (*red) = (unsigned char)(pow((double)(*red )/255.0,g)*255.0+0.5);
190 (*green) = (unsigned char)(pow((double)(*green)/255.0,g)*255.0+0.5);
191 (*blue) = (unsigned char)(pow((double)(*blue )/255.0,g)*255.0+0.5);
195 int PNGDIB_DECL pngdib_p2d_run(PNGDIB *qq)
197 struct p2d_struct *p2d;
199 png_structp png_ptr;
200 png_infop info_ptr;
201 jmp_buf jbuf;
202 struct errstruct errinfo;
203 png_uint_32 width, height;
204 int png_bit_depth, color_type, interlace_type;
205 png_colorp png_palette;
206 png_uint_32 res_x, res_y;
207 int has_phys, has_gama;
208 int res_unit_type;
209 FILE *fp;
210 int palette_entries;
211 unsigned char **row_pointers;
212 unsigned char *lpdib;
213 unsigned char *dib_palette;
214 unsigned char *dib_bits;
215 unsigned char *tmprow;
216 int dib_bpp, dib_bytesperrow;
217 int i,j;
218 int rv;
219 png_color_16 bkgd; // used with png_set_background
220 int has_trns, trns_color;
221 int has_bkgd; // ==1 if there a bkgd chunk, and USE_BKGD flag
222 png_color_16p temp_colorp;
223 png_color_16p bg_colorp; // background color (if has_bkgd)
224 png_bytep trns_trans;
225 int manual_trns;
226 int manual_gamma;
227 struct PNGD_COLOR_struct bkgd_color;
228 int is_grayscale,has_alpha_channel;
229 double file_gamma;
230 int dib_alpha32;
231 int write_bitfields;
233 p2d=(struct p2d_struct*)qq;
235 dib_alpha32=0;
236 write_bitfields=0;
239 manual_trns=0;
240 has_trns=has_bkgd=0;
241 rv=PNGD_E_ERROR;
242 png_ptr=NULL;
243 info_ptr=NULL;
244 fp=NULL;
245 row_pointers=NULL;
246 lpdib=NULL;
248 lstrcpy(p2d->common.errmsg,_T(""));
250 if(p2d->use_custom_bg_flag) {
251 bkgd_color.red= p2d->bgcolor.red;
252 bkgd_color.green= p2d->bgcolor.green;
253 bkgd_color.blue= p2d->bgcolor.blue;
255 else {
256 bkgd_color.red= 255; // Should never get used. If the
257 bkgd_color.green= 128; // background turns orange, it's a bug.
258 bkgd_color.blue= 0;
261 // Set the user-defined pointer to point to our jmp_buf. This will
262 // hopefully protect against potentially different sized jmp_buf's in
263 // libpng, while still allowing this library to be threadsafe.
264 errinfo.jbufp = &jbuf;
265 errinfo.errmsg = p2d->common.errmsg;
267 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,(void*)(&errinfo),
268 my_png_error_fn, my_png_warning_fn);
269 if(!png_ptr) { rv=PNGD_E_NOMEM; goto abort; }
272 info_ptr = png_create_info_struct(png_ptr);
273 if(!info_ptr) {
274 //png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
275 rv=PNGD_E_NOMEM; goto abort;
278 if(setjmp(jbuf)) {
279 // we'll get here if an error occurred in any of the following
280 // png_ functions
282 rv=PNGD_E_LIBPNG;
283 goto abort;
286 if(p2d->input_method==0) {
287 // reading from a filename
288 if(!p2d->input_filename) {
289 wsprintf(p2d->common.errmsg,_T("Input filename not set"));
290 rv=PNGD_E_ERROR; goto abort;
293 if((fp = _tfopen(p2d->input_filename,_T("rb"))) == NULL) {
294 rv=PNGD_E_READ;
295 goto abort;
297 png_init_io(png_ptr, fp);
299 else if(p2d->input_method==1) {
300 // reading from a memory block
301 p2d->input_memblk_curpos=0;
302 png_set_read_fn(png_ptr, (void*)p2d, my_png_read_fn);
304 else { goto abort; }
306 png_read_info(png_ptr, info_ptr);
308 png_get_IHDR(png_ptr, info_ptr, &width, &height, &png_bit_depth, &color_type,
309 &interlace_type, NULL, NULL);
311 p2d->color_type=color_type;
312 p2d->bits_per_sample=png_bit_depth;
313 p2d->interlace=interlace_type;
314 switch(color_type) {
315 case PNG_COLOR_TYPE_RGB: p2d->bits_per_pixel=png_bit_depth*3; break;
316 case PNG_COLOR_TYPE_RGB_ALPHA: p2d->bits_per_pixel=png_bit_depth*4; break;
317 case PNG_COLOR_TYPE_GRAY_ALPHA: p2d->bits_per_pixel=png_bit_depth*2; break;
318 default: p2d->bits_per_pixel=png_bit_depth;
321 is_grayscale = !(color_type&PNG_COLOR_MASK_COLOR);
322 has_alpha_channel = (color_type&PNG_COLOR_MASK_ALPHA)?1:0;
324 has_trns = png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS);
326 if(p2d->common.dib_alpha32 && (has_trns || has_alpha_channel)) {
327 // Fixme - if trns(for palette) has no transparent entries,
328 // we could skip this.
329 dib_alpha32=1;
330 write_bitfields=1;
332 if (!(color_type&PNG_COLOR_MASK_COLOR)) {
333 png_set_gray_to_rgb(png_ptr);
335 else if(color_type==PNG_COLOR_TYPE_PALETTE) {
336 png_set_palette_to_rgb(png_ptr);
339 if (has_trns) png_set_tRNS_to_alpha(png_ptr);
341 goto notrans;
344 // look for bKGD chunk, and process if applicable
345 if(p2d->use_file_bg_flag) {
346 if(png_get_bKGD(png_ptr, info_ptr, &bg_colorp)) {
347 // process the background, store 8-bit RGB in bkgd_color
348 has_bkgd=1;
350 if(is_grayscale && png_bit_depth<8) {
351 bkgd_color.red =
352 bkgd_color.green=
353 bkgd_color.blue =
354 (unsigned char) ( (bg_colorp->gray*255)/( (1<<png_bit_depth)-1 ) );
356 else if(png_bit_depth<=8) {
357 bkgd_color.red=(unsigned char)(bg_colorp->red);
358 bkgd_color.green=(unsigned char)(bg_colorp->green);
359 bkgd_color.blue =(unsigned char)(bg_colorp->blue);
361 else {
362 bkgd_color.red=(unsigned char)(bg_colorp->red>>8);
363 bkgd_color.green=(unsigned char)(bg_colorp->green>>8);
364 bkgd_color.blue =(unsigned char)(bg_colorp->blue>>8);
369 if( !(color_type & PNG_COLOR_MASK_ALPHA) && !has_trns) {
370 // If no transparency, we can skip this whole background-color mess.
371 goto notrans;
374 if(has_bkgd && (png_bit_depth>8 || !is_grayscale || has_alpha_channel)) {
375 png_set_background(png_ptr, bg_colorp,
376 PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
378 else if(is_grayscale && has_trns && png_bit_depth<=8
379 && (has_bkgd || (p2d->use_custom_bg_flag)) )
381 // grayscale binarytrans,<=8bpp: transparency is handle manually
382 // by modifying a palette entry (later)
383 png_get_tRNS(png_ptr,info_ptr,&trns_trans, &i, &temp_colorp);
384 if(i>=1) {
385 trns_color= temp_colorp->gray; // corresponds to a palette entry
386 manual_trns=1;
389 else if(!has_bkgd && (has_trns || has_alpha_channel) &&
390 (p2d->use_custom_bg_flag) )
391 { // process most CUSTOM background colors
392 bkgd.index = 0; // unused
393 bkgd.red = p2d->bgcolor.red;
394 bkgd.green = p2d->bgcolor.green;
395 bkgd.blue = p2d->bgcolor.blue;
397 // libpng may use bkgd.gray if bkgd.red==bkgd.green==bkgd.blue.
398 // Not sure if that's a libpng bug or not.
399 bkgd.gray = p2d->bgcolor.red;
401 if(png_bit_depth>8) {
402 bkgd.red = (bkgd.red <<8)|bkgd.red;
403 bkgd.green= (bkgd.green<<8)|bkgd.green;
404 bkgd.blue = (bkgd.blue <<8)|bkgd.blue;
405 bkgd.gray = (bkgd.gray <<8)|bkgd.gray;
408 if(is_grayscale) {
409 /* assert(png_bit_depth>8); */
411 /* Need to expand to full RGB if unless background is pure gray */
412 if(bkgd.red!=bkgd.green || bkgd.red!=bkgd.blue) {
413 png_set_gray_to_rgb(png_ptr);
415 // png_set_tRNS_to_alpha() is called here because otherwise
416 // binary transparency for 16-bps grayscale images doesn't
417 // work. Libpng will think black pixels are transparent.
418 // I don't know exactly why it works. It does *not* add an
419 // alpha channel, as you might think (adding an alpha
420 // channnel makes no sense if you are using
421 // png_set_background).
423 // Here's an alternate hack that also seems to work, but
424 // uses direct structure access:
426 // png_ptr->trans_values.red =
427 // png_ptr->trans_values.green =
428 // png_ptr->trans_values.blue = png_ptr->trans_values.gray;
429 if(has_trns)
430 png_set_tRNS_to_alpha(png_ptr);
432 png_set_background(png_ptr, &bkgd,
433 PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
436 else { // gray custom background
437 png_set_background(png_ptr, &bkgd,
438 PNG_BACKGROUND_GAMMA_SCREEN, 1, 1.0);
442 else {
443 png_set_background(png_ptr, &bkgd,
444 PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
448 notrans:
450 // If we don't have any background color at all that we can use,
451 // strip the alpha channel.
452 if(!dib_alpha32 && has_alpha_channel && !has_bkgd &&
453 !(p2d->use_custom_bg_flag) )
455 png_set_strip_alpha(png_ptr);
458 if(png_bit_depth>8)
459 png_set_strip_16(png_ptr);
461 if (png_get_sRGB(png_ptr, info_ptr, &i)) {
462 has_gama=1;
463 file_gamma = 0.45455;
465 else if(png_get_gAMA(png_ptr, info_ptr, &file_gamma)) {
466 has_gama=1;
468 else {
469 has_gama=0;
470 file_gamma = 0.45455;
473 if(/*imginfo && */ has_gama) {
474 p2d->file_gamma=file_gamma;
475 p2d->gamma_returned=1;
478 manual_gamma=0;
479 if(p2d->gamma_correction) {
481 if(!is_grayscale || png_bit_depth>8 || has_alpha_channel) {
482 png_set_gamma(png_ptr, p2d->screen_gamma, file_gamma);
483 //png_ptr->transformations |= 0x2000; // hack for old libpng versions
485 else manual_gamma=1;
487 if(has_bkgd) {
488 // Gamma correct the background color (if we got it from the file)
489 // before returning it to the app.
490 gamma_correct(p2d->screen_gamma,file_gamma,&bkgd_color.red,&bkgd_color.green,&bkgd_color.blue);
494 png_read_update_info(png_ptr, info_ptr);
496 // color type may have changed, due to our transformations
497 color_type = png_get_color_type(png_ptr,info_ptr);
500 switch(color_type) {
501 case PNG_COLOR_TYPE_RGB_ALPHA:
502 assert(dib_alpha32);
503 dib_bpp= 32;
504 palette_entries=0;
505 png_set_bgr(png_ptr);
506 break;
507 case PNG_COLOR_TYPE_RGB:
508 dib_bpp= 24;
509 palette_entries=0;
510 png_set_bgr(png_ptr);
511 break;
512 case PNG_COLOR_TYPE_PALETTE:
513 dib_bpp=png_bit_depth;
514 png_get_PLTE(png_ptr,info_ptr,&png_palette,&palette_entries);
515 break;
516 case PNG_COLOR_TYPE_GRAY:
517 case PNG_COLOR_TYPE_GRAY_ALPHA:
518 dib_bpp=png_bit_depth;
519 if(png_bit_depth>8) dib_bpp=8;
520 palette_entries= 1<<dib_bpp;
521 // we'll construct a grayscale palette later
522 break;
523 default:
524 rv=PNGD_E_BADPNG;
525 goto abort;
528 if(dib_bpp==2) dib_bpp=4;
530 has_phys=png_get_valid(png_ptr,info_ptr,PNG_INFO_pHYs);
531 if(has_phys) {
532 png_get_pHYs(png_ptr,info_ptr,&res_x,&res_y,&res_unit_type);
533 if(res_x>0 && res_y>0) {
534 p2d->res_x=res_x;
535 p2d->res_y=res_y;
536 p2d->res_units=res_unit_type;
537 p2d->res_valid=1;
541 // DIB scanlines are padded to 4-byte boundaries.
542 dib_bytesperrow= (((width * dib_bpp)+31)/32)*4;
544 p2d->bitssize = height*dib_bytesperrow;
546 p2d->dibsize=sizeof(BITMAPINFOHEADER) + 4*palette_entries +
547 (write_bitfields?12:0) + p2d->bitssize;;
549 if(p2d->common.malloc_function) {
550 lpdib = (unsigned char*)(*(p2d->common.malloc_function))(p2d->common.userdata,p2d->dibsize);
552 else {
553 lpdib = (unsigned char*)calloc(p2d->dibsize,1);
557 if(!lpdib) { rv=PNGD_E_NOMEM; goto abort; }
558 p2d->pdib = (LPBITMAPINFOHEADER)lpdib;
560 row_pointers=(unsigned char**)malloc(height*sizeof(unsigned char*));
561 if(!row_pointers) { rv=PNGD_E_NOMEM; goto abort; }
563 // there is some redundancy here...
564 p2d->palette_offs=sizeof(BITMAPINFOHEADER);
565 p2d->bits_offs =sizeof(BITMAPINFOHEADER) + 4*palette_entries + (write_bitfields?12:0);
566 dib_palette= &lpdib[p2d->palette_offs];
567 p2d->palette= (RGBQUAD*)dib_palette;
568 dib_bits = &lpdib[p2d->bits_offs];
569 p2d->pbits = (VOID*)dib_bits;
570 p2d->palette_colors = palette_entries;
572 // set up the DIB palette, if needed
573 switch(color_type) {
574 case PNG_COLOR_TYPE_PALETTE:
575 for(i=0;i<palette_entries;i++) {
576 p2d->palette[i].rgbRed = png_palette[i].red;
577 p2d->palette[i].rgbGreen = png_palette[i].green;
578 p2d->palette[i].rgbBlue = png_palette[i].blue;
580 break;
581 case PNG_COLOR_TYPE_GRAY:
582 case PNG_COLOR_TYPE_GRAY_ALPHA:
583 for(i=0;i<palette_entries;i++) {
584 p2d->palette[i].rgbRed =
585 p2d->palette[i].rgbGreen =
586 p2d->palette[i].rgbBlue = (i*255)/(palette_entries-1);
587 if(manual_gamma) {
588 gamma_correct(p2d->screen_gamma,file_gamma,
589 &(p2d->palette[i].rgbRed),
590 &(p2d->palette[i].rgbGreen),
591 &(p2d->palette[i].rgbBlue));
594 if(manual_trns) {
595 p2d->palette[trns_color].rgbRed = bkgd_color.red;
596 p2d->palette[trns_color].rgbGreen = bkgd_color.green;
597 p2d->palette[trns_color].rgbBlue = bkgd_color.blue;
599 break;
602 // set up BITFIELDS, if needed
603 if(dib_alpha32) {
604 static const unsigned char bf_data[12] = {
605 0x00,0x00,0xff,0x00,
606 0x00,0xff,0x00,0x00,
607 0xff,0x00,0x00,0x00
610 for(i=0;i<12;i++) {
611 lpdib[p2d->palette_offs+i]=bf_data[i];
616 for(j=0;j<(int)height;j++) {
617 row_pointers[height-1-j]= &dib_bits[j*dib_bytesperrow];
620 png_read_image(png_ptr, row_pointers);
622 // special handling for this bit depth, since it doesn't exist in DIBs
623 // expand 2bpp to 4bpp
624 if(png_bit_depth==2) {
625 tmprow = (unsigned char*)malloc((width+3)/4 );
626 if(!tmprow) { rv=PNGD_E_NOMEM; goto abort; }
628 for(j=0;j<(int)height;j++) {
629 CopyMemory(tmprow, row_pointers[j], (width+3)/4 );
630 ZeroMemory(row_pointers[j], (width+1)/2 );
632 for(i=0;i<(int)width;i++) {
633 row_pointers[j][i/2] |=
634 ( ((tmprow[i/4] >> (2*(3-i%4)) ) & 0x03)<< (4*(1-i%2)) );
637 free((void*)tmprow);
640 free((void*)row_pointers);
641 row_pointers=NULL;
643 png_read_end(png_ptr, info_ptr);
645 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
646 png_ptr=NULL;
648 if(p2d->input_method==0) {
649 fclose(fp);
650 fp=NULL;
653 // fill in the DIB header fields
654 p2d->pdib->biSize= sizeof(BITMAPINFOHEADER);
655 p2d->pdib->biWidth= width;
656 p2d->pdib->biHeight= height;
657 p2d->pdib->biPlanes= 1;
658 p2d->pdib->biBitCount= dib_bpp;
659 p2d->pdib->biCompression= write_bitfields?BI_BITFIELDS:BI_RGB;
660 // biSizeImage can also be 0 in uncompressed bitmaps
661 p2d->pdib->biSizeImage= height*dib_bytesperrow;
663 if(has_phys) {
664 if(res_unit_type==1) {
665 p2d->pdib->biXPelsPerMeter= res_x;
666 p2d->pdib->biYPelsPerMeter= res_y;
669 p2d->pdib->biClrUsed= palette_entries;
670 p2d->pdib->biClrImportant= 0;
672 if(has_bkgd || (p2d->use_custom_bg_flag)) {
673 // return the background color if one was used
674 p2d->bgcolor.red = bkgd_color.red;
675 p2d->bgcolor.green = bkgd_color.green;
676 p2d->bgcolor.blue = bkgd_color.blue;
677 p2d->bgcolor_returned=1;
680 return PNGD_E_SUCCESS;
682 abort:
684 if(png_ptr) png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
685 if(p2d->input_method==0 && fp) fclose(fp);
686 if(row_pointers) free((void*)row_pointers);
687 if(lpdib) {
688 pngdib_p2d_free_dib((PNGDIB*)p2d,NULL);
691 // If we don't have an error message yet, use a
692 // default one based on the code
693 if(!lstrlen(p2d->common.errmsg)) {
694 pngd_get_error_message(rv,p2d->common.errmsg);
697 return rv;
700 void PNGDIB_DECL pngdib_p2d_free_dib(PNGDIB *qq, BITMAPINFOHEADER* pdib)
702 struct p2d_struct *p2d;
704 if(!qq) {
705 if(pdib) free((void*)pdib);
706 return;
709 p2d=(struct p2d_struct*)qq;
710 if(!pdib) {
711 // DIB not explicitly given; use the one from the PNGDIB object.
712 // (this is the normal case)
713 pdib = p2d->pdib;
714 p2d->pdib = NULL;
716 if(pdib) {
717 if(p2d->common.free_function) {
718 (*(p2d->common.free_function))(p2d->common.userdata,(void*)pdib);
720 else {
721 free((void*)pdib);
726 int PNGDIB_DECL pngdib_d2p_run(PNGDIB *qq)
728 struct d2p_struct *d2p;
729 png_structp png_ptr;
730 png_infop info_ptr;
731 jmp_buf jbuf;
732 struct errstruct errinfo;
733 png_text text_ptr[1];
734 unsigned char *bits;
735 unsigned char *newimage;
736 RGBQUAD* dib_palette;
737 int headersize, dib_bpp;
738 int png_color_type;
739 png_uint_32 res_x, res_y;
740 png_color_8 pngc8;
741 int height,width;
742 int palette_entries;
743 int topdown;
744 int dib_bytesperrow;
745 int compression;
746 FILE *fp;
747 png_color png_palette[256];
748 unsigned char **row_pointers;
749 int i,x,y,size;
750 DWORD *bitfields;
751 unsigned int v;
752 int bf_format; // bitfields format identifier
753 int iscompressed;
754 int bfsize; // bytes in "bitfields" section
755 int palsize; // bytes in palette
757 int palentsize; // bytes in a palette entry: 3 or 4;
758 BITMAPCOREHEADER *lpolddib;
759 int rv; // return code
760 int dib_alpha32;
762 d2p=(struct d2p_struct*)qq;
764 rv=PNGD_E_ERROR; // this should always get changed before returning
765 png_ptr=NULL;
766 info_ptr=NULL;
767 fp=NULL;
768 row_pointers=NULL;
769 newimage=NULL;
770 dib_alpha32=0;
772 lstrcpy(d2p->common.errmsg,_T(""));
774 if(!d2p->output_filename) {
775 wsprintf(d2p->common.errmsg,_T("Output filename not set"));
776 rv=PNGD_E_ERROR; goto abort;
779 if(!d2p->pdib) {
780 wsprintf(d2p->common.errmsg,_T("Input DIB not set"));
781 rv=PNGD_E_ERROR; goto abort;
785 headersize= d2p->pdib->biSize;
787 if(headersize<40 && headersize!=12) {
788 wsprintf(d2p->common.errmsg,_T("Unexpected BMP header size (%d)"),headersize);
789 rv=PNGD_E_BADBMP; goto abort;
792 if(headersize==12) {
793 // This is to support an old kind of DIBs (OS/2) that aren't really
794 // used anymore.
795 palentsize= 3;
796 lpolddib= (BITMAPCOREHEADER*) d2p->pdib;
797 width= lpolddib->bcWidth;
798 height= lpolddib->bcHeight;
799 dib_bpp= lpolddib->bcBitCount;
800 compression = BI_RGB;
801 res_x = res_y = 0;
803 // This will get adjusted later if there is a palette.
804 // Not sure it's right, though. Do old DIBs always have a
805 // full-sized palette?
806 palette_entries=0;
808 else {
809 palentsize=4;
810 width = d2p->pdib->biWidth;
811 height = d2p->pdib->biHeight;
812 dib_bpp = d2p->pdib->biBitCount;
813 compression = d2p->pdib->biCompression;
814 res_x = d2p->pdib->biXPelsPerMeter;
815 res_y = d2p->pdib->biYPelsPerMeter;
816 palette_entries = d2p->pdib->biClrUsed;
819 // supposedly, if the height is negative, the top scanline is stored first
820 topdown=0;
821 if(height<0) {
822 height= -height;
823 topdown=1;
826 // sanity check
827 if(height<1 || height>1000000 || width<1 || width>1000000) {
828 wsprintf(d2p->common.errmsg,_T("Unreasonable image dimensions (%dx%d)"),width,height);
829 rv=PNGD_E_BADBMP; goto abort;
832 if(dib_bpp==32 && (d2p->common.dib_alpha32)) {
833 dib_alpha32=1;
836 // only certain combinations of compression and bpp are allowed
837 switch(compression) {
838 case BI_RGB:
839 if(dib_bpp!=1 && dib_bpp!=4 && dib_bpp!=8 && dib_bpp!=16
840 && dib_bpp!=24 && dib_bpp!=32)
842 wsprintf(d2p->common.errmsg,_T("Unsupported bit depth (%d)"),dib_bpp);
843 rv=PNGD_E_UNSUPP; goto abort;
845 break;
846 case BI_RLE4:
847 if(dib_bpp!=4) {
848 rv=PNGD_E_UNSUPP; goto abort;
850 break;
851 case BI_RLE8:
852 if(dib_bpp!=8) {
853 rv=PNGD_E_UNSUPP; goto abort;
855 break;
856 case BI_BITFIELDS:
857 if(dib_bpp!=16 && dib_bpp!=32) {
858 rv=PNGD_E_UNSUPP; goto abort;
860 break;
861 default:
862 wsprintf(d2p->common.errmsg,_T("Unsupported compression scheme"));
863 return PNGD_E_UNSUPP;
866 iscompressed= (compression==BI_RLE4 || compression==BI_RLE8);
868 // uncompressed dibs are padded to 4-byte bondaries
869 dib_bytesperrow= (((width * dib_bpp)+31)/32)*4;
871 if(dib_bpp<16) {
872 if(palette_entries==0) palette_entries = 1<<dib_bpp;
875 bfsize = (compression==BI_BITFIELDS)?12:0;
877 palsize=palentsize*palette_entries;
879 // bounds check
880 size= headersize + bfsize + palsize;
881 if(d2p->dibsize) {
882 if(size>d2p->dibsize) {
883 rv=PNGD_E_BADBMP; goto abort;
887 if(d2p->pbits) {
888 if(d2p->bitssize && !iscompressed) { // bounds check
889 size=dib_bytesperrow*height;
890 if(size>d2p->bitssize) { rv=PNGD_E_BADBMP; goto abort; }
893 bits=(unsigned char*)d2p->pbits;
895 else {
896 // If not provided by user, assume the bits immediately
897 // follow the palette.
899 if(d2p->dibsize && !iscompressed) { // bounds check
900 size= headersize+bfsize+palsize+dib_bytesperrow*height;
901 if(size>d2p->dibsize) { rv=PNGD_E_BADBMP; goto abort; }
904 bits= &((unsigned char*)(d2p->pdib))[headersize+bfsize+palsize];
907 bitfields = (DWORD*) ( &((unsigned char*)(d2p->pdib))[headersize] );
908 dib_palette = (RGBQUAD*) ( &((unsigned char*)(d2p->pdib))[headersize+bfsize] );
910 bf_format=0;
911 if(compression==BI_BITFIELDS) {
912 if(dib_bpp==16) {
913 if (bitfields[0]==0x00007c00 && bitfields[1]==0x000003e0 &&
914 bitfields[2]==0x0000001f) bf_format=11; // 555
915 else if(bitfields[0]==0x0000f800 && bitfields[1]==0x000007e0 &&
916 bitfields[2]==0x0000001f) bf_format=12; // 565
917 else { rv=PNGD_E_UNSUPP; goto abort; }
919 if(dib_bpp==32) {
920 if (bitfields[0]==0x00ff0000 && bitfields[1]==0x0000ff00 &&
921 bitfields[2]==0x000000ff) bf_format=21;
922 else { rv=PNGD_E_UNSUPP; goto abort; }
926 if(bf_format==0 && dib_bpp==16) bf_format=10;
927 if(bf_format==0 && dib_bpp==32) bf_format=20;
930 // Done analyzing the DIB, now time to convert it to PNG
932 // FIXME: this probably isn't quite right
933 // jbuf: see comments in read_png_to_dib()
934 errinfo.jbufp = &jbuf;
935 errinfo.errmsg = d2p->common.errmsg;
936 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (void*)(&errinfo),
937 my_png_error_fn, my_png_warning_fn);
938 if (!png_ptr) { rv=PNGD_E_NOMEM; goto abort; }
941 info_ptr = png_create_info_struct(png_ptr);
942 if (!info_ptr) {
943 //png_destroy_write_struct(&png_ptr,(png_infopp)NULL);
944 rv=PNGD_E_NOMEM; goto abort;
947 if(setjmp(jbuf)) {
948 // we'll get here if an error occurred in any of the following
949 // png_ functions
950 rv=PNGD_E_LIBPNG;
951 goto abort;
954 fp= _tfopen(d2p->output_filename,_T("wb"));
955 if(!fp) {
956 rv=PNGD_E_WRITE;
957 goto abort;
960 png_init_io(png_ptr, fp);
962 if(dib_alpha32) {
963 png_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
965 else {
966 png_color_type = (dib_bpp>8)?PNG_COLOR_TYPE_RGB:PNG_COLOR_TYPE_PALETTE;
969 png_set_IHDR(png_ptr, info_ptr, width, height, (dib_bpp>8)?8:dib_bpp,
970 png_color_type,
971 (d2p->interlaced)?PNG_INTERLACE_ADAM7:PNG_INTERLACE_NONE,
972 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
974 // write sRGB and gAMA chunks
975 if(d2p->file_gamma_valid) {
976 if(d2p->file_gamma>0.454539 && d2p->file_gamma<0.454551) {
977 png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_RELATIVE);
979 png_set_gAMA(png_ptr, info_ptr, d2p->file_gamma);
982 // For 16-bit DIBs, we get to write an sBIT chunk.
983 if(bf_format==10 || bf_format==11) {
984 pngc8.red= 5; pngc8.green= 5; pngc8.blue= 5;
985 png_set_sBIT(png_ptr, info_ptr, &pngc8);
987 if(bf_format==12) {
988 pngc8.red= 5; pngc8.green= 6; pngc8.blue= 5;
989 png_set_sBIT(png_ptr, info_ptr, &pngc8);
992 // pHYs
993 if(res_x>0 && res_y>0)
994 png_set_pHYs(png_ptr, info_ptr, res_x, res_y, 1);
997 if(palette_entries>0) {
998 for(i=0;i<palette_entries;i++) {
999 if(palentsize==3) {
1000 png_palette[i].red = ((RGBTRIPLE*)dib_palette)[i].rgbtRed;
1001 png_palette[i].green = ((RGBTRIPLE*)dib_palette)[i].rgbtGreen;
1002 png_palette[i].blue = ((RGBTRIPLE*)dib_palette)[i].rgbtBlue;
1004 else {
1005 png_palette[i].red = dib_palette[i].rgbRed;
1006 png_palette[i].green = dib_palette[i].rgbGreen;
1007 png_palette[i].blue = dib_palette[i].rgbBlue;
1010 png_set_PLTE(png_ptr, info_ptr, png_palette, palette_entries);
1013 if(dib_bpp>8)
1014 png_set_bgr(png_ptr);
1016 png_write_info(png_ptr, info_ptr);
1018 row_pointers=(unsigned char**)malloc(height*sizeof(unsigned char*));
1019 if(!row_pointers) { rv=PNGD_E_NOMEM; goto abort; }
1022 if(dib_bpp==16 || (dib_bpp==32 && !dib_alpha32)) {
1024 // Special handling for these bit depths.
1025 // This uses a lot of memory, and could be improved by processing
1026 // one line at a time (but that makes it tricky to write interlaced
1027 // images).
1029 newimage=(unsigned char*)malloc(height*width*3);
1030 if(!newimage) { rv=PNGD_E_NOMEM; goto abort; }
1032 for(y=0;y<height;y++) {
1033 for(x=0;x<width;x++) {
1034 switch(bf_format) {
1035 case 10: case 11: // 16-bit, format 555 (xRRRRRGG GGGBBBBB)
1036 v= bits[y*dib_bytesperrow+x*2+0] | (bits[y*dib_bytesperrow+x*2+1]<<8);
1037 newimage[(y*width+x)*3+0]= (v & 0x0000001f)<<3 | (v & 0x0000001f)>>2; // blue
1038 newimage[(y*width+x)*3+1]= (v & 0x000003e0)>>2 | (v & 0x000003e0)>>7; // green
1039 newimage[(y*width+x)*3+2]= (v & 0x00007c00)>>7 | (v & 0x00007c00)>>12; // red
1040 break;
1041 case 12: // 16-bit, format 565 (RRRRRGGG GGGBBBBB)
1042 v= bits[y*dib_bytesperrow+x*2+0] | (bits[y*dib_bytesperrow+x*2+1]<<8);
1043 newimage[(y*width+x)*3+0]= (v & 0x0000001f)<<3 | (v & 0x0000001f)>>2; // blue
1044 newimage[(y*width+x)*3+1]= (v & 0x000007e0)>>3 | (v & 0x000007e0)>>9; // green
1045 newimage[(y*width+x)*3+2]= (v & 0x0000f800)>>8 | (v & 0x0000f800)>>13; // red
1046 break;
1047 case 20: case 21: // 32-bit, every 4th byte wasted (b g r x)
1048 newimage[(y*width+x)*3+0]= bits[y*dib_bytesperrow+x*4+0]; // blue
1049 newimage[(y*width+x)*3+1]= bits[y*dib_bytesperrow+x*4+1]; // green
1050 newimage[(y*width+x)*3+2]= bits[y*dib_bytesperrow+x*4+2]; // red
1051 break;
1056 for(i=0;i<height;i++) {
1057 if(topdown)
1058 row_pointers[i]= &newimage[i*width*3];
1059 else
1060 row_pointers[height-1-i]= &newimage[i*width*3];
1062 png_write_image(png_ptr, row_pointers);
1064 free(newimage);
1065 newimage=NULL;
1067 else if(iscompressed) {
1068 newimage= uncompress_dib((LPBITMAPINFO)d2p->pdib, headersize+bfsize+palsize, bits);
1069 if(!newimage) { rv=PNGD_E_NOMEM; goto abort; }
1070 for(i=0;i<height;i++) {
1071 if(topdown)
1072 row_pointers[i]= &newimage[i*dib_bytesperrow];
1073 else
1074 row_pointers[height-1-i]= &newimage[i*dib_bytesperrow];
1076 png_write_image(png_ptr, row_pointers);
1078 free(newimage);
1079 newimage=NULL;
1081 else {
1082 for(i=0;i<height;i++) {
1083 if(topdown)
1084 row_pointers[i]= &bits[i*dib_bytesperrow];
1085 else
1086 row_pointers[height-1-i]= &bits[i*dib_bytesperrow];
1088 png_write_image(png_ptr, row_pointers);
1091 free((VOID*)row_pointers);
1092 row_pointers=NULL;
1094 if(d2p->software_id_string) {
1095 text_ptr[0].key = "Software";
1096 text_ptr[0].text = d2p->software_id_string;
1097 text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE;
1098 png_set_text(png_ptr, info_ptr, text_ptr, 1);
1101 png_write_end(png_ptr, info_ptr);
1103 rv=PNGD_E_SUCCESS;
1105 abort:
1106 if(png_ptr) png_destroy_write_struct(&png_ptr, &info_ptr);
1107 if(fp) fclose(fp);
1108 if(row_pointers) free(row_pointers);
1109 if(newimage) free(newimage);
1111 // If we don't have an error message yet, use a
1112 // default one based on the code
1113 if(!lstrlen(d2p->common.errmsg)) {
1114 pngd_get_error_message(rv,d2p->common.errmsg);
1116 return rv;
1119 TCHAR* PNGDIB_DECL pngdib_get_version_string(void)
1121 return PNGDIB_SRC_VERSION_STRING;
1124 int PNGDIB_DECL pngdib_get_version(void)
1126 return PNGDIB_SRC_VERSION;
1130 PNGDIB* PNGDIB_DECL _pngdib_init(int structtype, int caller_header_vers)
1132 PNGDIB *qq = NULL;
1134 if(structtype==PNGD_ST_D2P) {
1135 struct d2p_struct *d2p;
1137 d2p = (struct d2p_struct *)calloc(sizeof(struct d2p_struct),1);
1138 if(d2p) {
1139 d2p->common.structtype = PNGD_ST_D2P;
1140 d2p->file_gamma_valid = 1;
1141 d2p->file_gamma = PNGDIB_DEFAULT_FILE_GAMMA;
1143 qq=(PNGDIB*)d2p;
1146 else if(structtype==PNGD_ST_P2D) {
1147 struct p2d_struct *p2d;
1149 p2d = (struct p2d_struct *)calloc(sizeof(struct p2d_struct),1);
1150 if(p2d) {
1151 p2d->common.structtype = PNGD_ST_P2D;
1153 qq=(PNGDIB*)p2d;
1156 // initialize common fields:
1157 if(qq) {
1158 qq->errmsg = calloc(PNGDIB_ERRMSG_MAX,sizeof(TCHAR));
1161 return qq;
1165 int PNGDIB_DECL pngdib_d2p_set_dib(PNGDIB *qq,
1166 const BITMAPINFOHEADER* lpdib, int dibsize,
1167 const void* lpbits, int bitssize)
1169 struct d2p_struct *d2p;
1170 if(qq->structtype!=PNGD_ST_D2P) return 0;
1171 d2p=(struct d2p_struct*)qq;
1173 d2p->pdib = lpdib;
1174 d2p->dibsize = dibsize;
1175 d2p->pbits = lpbits;
1176 d2p->bitssize = bitssize;
1177 return 1;
1180 void PNGDIB_DECL pngdib_d2p_set_interlace(PNGDIB *qq, int interlaced)
1182 struct d2p_struct *d2p;
1183 d2p=(struct d2p_struct*)qq;
1184 d2p->interlaced = interlaced;
1187 int PNGDIB_DECL pngdib_d2p_set_png_filename(PNGDIB *qq, const TCHAR *fn)
1189 struct d2p_struct *d2p;
1190 d2p=(struct d2p_struct*)qq;
1191 d2p->output_filename = _tcsdup(fn);
1192 return (d2p->output_filename)?1:0;
1195 int PNGDIB_DECL pngdib_d2p_set_software_id(PNGDIB *qq, const TCHAR *s)
1197 struct d2p_struct *d2p;
1198 int len;
1200 d2p=(struct d2p_struct*)qq;
1201 len= lstrlen(s);
1203 // The software id is never stored as UNICODE.
1204 #ifdef _UNICODE
1205 d2p->software_id_string = calloc(len+10,1);
1206 _snprintf(d2p->software_id_string,len+10,"%S",s);
1207 #else
1208 d2p->software_id_string = _strdup(s);
1209 #endif
1211 return (d2p->output_filename)?1:0;
1215 void PNGDIB_DECL pngdib_d2p_set_gamma_label(PNGDIB *qq, int flag, double file_gamma)
1217 struct d2p_struct *d2p;
1218 d2p=(struct d2p_struct*)qq;
1219 d2p->file_gamma_valid = flag;
1220 d2p->file_gamma = file_gamma;
1223 int PNGDIB_DECL pngdib_done(PNGDIB *qq)
1225 struct d2p_struct *d2p;
1226 struct p2d_struct *p2d;
1228 if(!qq) return 0;
1230 if(qq->errmsg) free(qq->errmsg);
1232 switch(qq->structtype) {
1233 case PNGD_ST_D2P:
1234 d2p=(struct d2p_struct*)qq;
1235 if(d2p->output_filename) free(d2p->output_filename);
1236 if(d2p->software_id_string) free(d2p->software_id_string);
1237 free(d2p);
1238 return 1;
1239 case PNGD_ST_P2D:
1240 p2d=(struct p2d_struct*)qq;
1241 if(p2d->input_filename) free(p2d->input_filename);
1242 free(p2d);
1243 return 1;
1245 return 0;
1248 void PNGDIB_DECL pngdib_setcallback_malloc(PNGDIB *qq,
1249 pngdib_malloc_cb_type mallocfunc,
1250 pngdib_free_cb_type freefunc,
1251 pngdib_realloc_cb_type reallocfunc)
1253 qq->malloc_function = mallocfunc;
1254 qq->free_function = freefunc;
1255 qq->realloc_function = reallocfunc;
1259 TCHAR* PNGDIB_DECL pngdib_get_error_msg(PNGDIB *qq)
1261 return qq->errmsg;
1264 void PNGDIB_DECL pngdib_set_userdata(PNGDIB* qq, void* userdata)
1266 qq->userdata = userdata;
1269 void* PNGDIB_DECL pngdib_get_userdata(PNGDIB* qq)
1271 return qq->userdata;
1275 int PNGDIB_DECL pngdib_p2d_set_png_filename(PNGDIB *qq, const TCHAR *fn)
1277 struct p2d_struct *p2d;
1278 p2d=(struct p2d_struct*)qq;
1279 p2d->input_filename = _tcsdup(fn);
1280 p2d->input_method = 0;
1281 return (p2d->input_filename)?1:0;
1284 void PNGDIB_DECL pngdib_p2d_set_png_memblk(PNGDIB *qq, const void *mem, int memsize)
1286 struct p2d_struct *p2d;
1287 p2d=(struct p2d_struct*)qq;
1288 p2d->input_memblk = (unsigned char*)mem;
1289 p2d->input_method = 1;
1290 if(memsize>=0)
1291 p2d->input_memblk_size = memsize;
1294 void PNGDIB_DECL pngdib_p2d_set_use_file_bg(PNGDIB *qq, int flag)
1296 struct p2d_struct *p2d;
1297 p2d=(struct p2d_struct*)qq;
1298 p2d->use_file_bg_flag = flag;
1302 void PNGDIB_DECL pngdib_p2d_set_custom_bg(PNGDIB *qq, unsigned char r,
1303 unsigned char g, unsigned char b)
1305 struct p2d_struct *p2d;
1306 p2d=(struct p2d_struct*)qq;
1307 p2d->bgcolor.red = r;
1308 p2d->bgcolor.green = g;
1309 p2d->bgcolor.blue = b;
1310 p2d->use_custom_bg_flag = 1;
1313 void PNGDIB_DECL pngdib_p2d_set_gamma_correction(PNGDIB *qq, int flag, double screen_gamma)
1315 struct p2d_struct *p2d;
1316 p2d=(struct p2d_struct*)qq;
1317 p2d->screen_gamma = screen_gamma;
1318 p2d->gamma_correction = flag;
1322 int PNGDIB_DECL pngdib_p2d_get_dib(PNGDIB *qq,
1323 BITMAPINFOHEADER **ppdib, int *pdibsize)
1325 struct p2d_struct *p2d;
1326 p2d=(struct p2d_struct*)qq;
1327 *ppdib = p2d->pdib;
1328 if(pdibsize) *pdibsize = p2d->dibsize;
1329 return 1;
1332 int PNGDIB_DECL pngdib_p2d_get_dibbits(PNGDIB *qq, void **ppbits, int *pbitsoffset, int *pbitssize)
1334 struct p2d_struct *p2d;
1335 p2d=(struct p2d_struct*)qq;
1336 *ppbits = p2d->pbits;
1337 if(pbitsoffset) *pbitsoffset = p2d->bits_offs;
1338 if(pbitssize) *pbitssize = p2d->bitssize;
1339 return 1;
1342 int PNGDIB_DECL pngdib_p2d_get_palette(PNGDIB *qq, RGBQUAD **ppal, int *ppaloffset, int *ppalnumcolors)
1344 struct p2d_struct *p2d;
1345 p2d=(struct p2d_struct*)qq;
1346 if(ppal) *ppal = p2d->palette;
1347 if(ppaloffset) *ppaloffset = p2d->palette_offs;
1348 if(ppalnumcolors) *ppalnumcolors = p2d->palette_colors;
1349 return 1;
1352 int PNGDIB_DECL pngdib_p2d_get_colortype(PNGDIB *qq)
1354 struct p2d_struct *p2d;
1355 p2d=(struct p2d_struct*)qq;
1356 return p2d->color_type;
1359 int PNGDIB_DECL pngdib_p2d_get_bitspersample(PNGDIB *qq)
1361 struct p2d_struct *p2d;
1362 p2d=(struct p2d_struct*)qq;
1363 return p2d->bits_per_sample;
1366 int PNGDIB_DECL pngdib_p2d_get_bitsperpixel(PNGDIB *qq)
1368 struct p2d_struct *p2d;
1369 p2d=(struct p2d_struct*)qq;
1370 return p2d->bits_per_pixel;
1373 int PNGDIB_DECL pngdib_p2d_get_samplesperpixel(PNGDIB *qq)
1375 struct p2d_struct *p2d;
1376 p2d=(struct p2d_struct*)qq;
1377 return p2d->bits_per_pixel/p2d->bits_per_sample;
1380 int PNGDIB_DECL pngdib_p2d_get_interlace(PNGDIB *qq)
1382 struct p2d_struct *p2d;
1383 p2d=(struct p2d_struct*)qq;
1384 return p2d->interlace;
1387 int PNGDIB_DECL pngdib_p2d_get_density(PNGDIB *qq, int *pres_x, int *pres_y, int *pres_units)
1389 struct p2d_struct *p2d;
1390 p2d=(struct p2d_struct*)qq;
1391 if(p2d->res_valid) {
1392 *pres_x = p2d->res_x;
1393 *pres_y = p2d->res_y;
1394 *pres_units = p2d->res_units;
1395 return 1;
1397 *pres_x = 1;
1398 *pres_y = 1;
1399 *pres_units = 0;
1400 return 0;
1403 int PNGDIB_DECL pngdib_p2d_get_file_gamma(PNGDIB *qq, double *pgamma)
1405 struct p2d_struct *p2d;
1406 p2d=(struct p2d_struct*)qq;
1407 if(p2d->gamma_returned) {
1408 *pgamma = p2d->file_gamma;
1409 return 1;
1411 return 0;
1414 int PNGDIB_DECL pngdib_p2d_get_bgcolor(PNGDIB *qq, unsigned char *pr, unsigned char *pg, unsigned char *pb)
1416 struct p2d_struct *p2d;
1417 p2d=(struct p2d_struct*)qq;
1419 if(p2d->bgcolor_returned) {
1420 *pr = p2d->bgcolor.red;
1421 *pg = p2d->bgcolor.green;
1422 *pb = p2d->bgcolor.blue;
1423 return 1;
1425 return 0;
1428 void PNGDIB_DECL pngdib_set_dibalpha32(PNGDIB *qq, int flag)
1430 qq->dib_alpha32 = flag;
1434 ////////////////////////////////////
1435 ////////////////////////////////////
1437 #if PNGDIB_V2COMPATIBLE
1439 static void* PNGDIB_DECL globalalloc_callback(void *userdata, int memblksize)
1441 return (void*)GlobalAlloc(GPTR,memblksize);
1444 static void PNGDIB_DECL globalfree_callback(void *userdata, void *memblk)
1446 GlobalFree((HGLOBAL)memblk);
1450 static void* PNGDIB_DECL heapalloc_callback(void *userdata, int memblksize)
1452 return HeapAlloc((HANDLE)userdata,HEAP_ZERO_MEMORY,memblksize);
1455 static void PNGDIB_DECL heapfree_callback(void *userdata, void *memblk)
1457 HeapFree((HANDLE)userdata,0,memblk);
1461 int read_png_to_dib(PNGD_P2DINFO *p2dp)
1463 PNGDIB *qq;
1464 int errcode;
1465 char *msg;
1466 HANDLE heap;
1467 int use_heapalloc;
1468 int imginfo;
1470 imginfo=0;
1471 use_heapalloc=0;
1474 qq=pngdib_p2d_init();
1475 if(!qq) return PNGD_E_NOMEM;
1478 // fields through errmsg must exist
1479 if(p2dp->structsize<48) return PNGD_E_VERSION;
1481 // try to be somewhat backward-compatible
1482 if(p2dp->structsize>=88) {
1483 imginfo=1;
1486 if(p2dp->structsize>=96) {
1487 if(p2dp->flags & PNGD_USE_HEAPALLOC) {
1488 use_heapalloc=1;
1489 heap = p2dp->heap;
1490 if(!heap) heap = GetProcessHeap();
1494 if(use_heapalloc) {
1495 pngdib_set_userdata(qq,(void*)heap);
1496 pngdib_setcallback_malloc(qq,heapalloc_callback,heapfree_callback,NULL);
1498 else {
1499 pngdib_setcallback_malloc(qq,globalalloc_callback,globalfree_callback,NULL);
1502 pngdib_p2d_set_png_filename(qq,p2dp->pngfn);
1504 pngdib_p2d_set_gamma_correction(qq,(p2dp->flags&PNGD_GAMMA_CORRECTION)?1:0,PNGDIB_DEFAULT_SCREEN_GAMMA);
1506 if(p2dp->flags&PNGD_USE_BKGD)
1507 pngdib_p2d_set_use_file_bg(qq,1);
1510 if(p2dp->flags&PNGD_USE_CUSTOM_BG) {
1511 pngdib_p2d_set_custom_bg(qq,p2dp->bgcolor.red,
1512 p2dp->bgcolor.green,p2dp->bgcolor.blue);
1515 if(p2dp->flags&PNGD_DIB_ALPHA32)
1516 pngdib_set_dibalpha32(qq,1);
1518 errcode=pngdib_p2d_run(qq);
1520 if(!errcode) {
1522 pngdib_p2d_get_dib(qq,&p2dp->lpdib, &p2dp->dibsize);
1523 pngdib_p2d_get_dibbits(qq,&p2dp->lpbits, &p2dp->bits_offs, NULL);
1524 pngdib_p2d_get_palette(qq,&p2dp->palette, &p2dp->palette_offs, &p2dp->palette_colors);
1526 if(imginfo) {
1527 p2dp->color_type = pngdib_p2d_get_colortype(qq);
1528 p2dp->bits_per_sample = pngdib_p2d_get_bitspersample(qq);
1529 p2dp->interlace = pngdib_p2d_get_interlace(qq);
1530 p2dp->bits_per_pixel = pngdib_p2d_get_bitsperpixel(qq);
1531 if(pngdib_p2d_get_file_gamma(qq, &p2dp->file_gamma)) {
1532 p2dp->flags |= PNGD_GAMMA_RETURNED;
1534 if(pngdib_p2d_get_density(qq, &p2dp->res_x, &p2dp->res_y, &p2dp->res_units)) {
1535 p2dp->flags |= PNGD_RES_RETURNED;
1537 if(pngdib_p2d_get_bgcolor(qq, &p2dp->bgcolor.red, &p2dp->bgcolor.green,
1538 &p2dp->bgcolor.blue))
1540 p2dp->flags |= PNGD_BG_RETURNED;
1545 if(p2dp->errmsg) {
1546 msg=pngdib_get_error_msg(qq);
1547 lstrcpyn(p2dp->errmsg,msg,100);
1550 pngdib_done(qq);
1552 return errcode;
1555 int write_dib_to_png(PNGD_D2PINFO *d2pp)
1557 PNGDIB *qq;
1558 int errcode;
1559 char *msg;
1561 if(d2pp->structsize != sizeof(PNGD_D2PINFO)) return PNGD_E_VERSION;
1563 qq=pngdib_d2p_init();
1564 if(!qq) return PNGD_E_NOMEM;
1565 pngdib_d2p_set_dib(qq,d2pp->lpdib,d2pp->dibsize,
1566 d2pp->lpbits,d2pp->bitssize);
1567 if(d2pp->flags&PNGD_INTERLACED)
1568 pngdib_d2p_set_interlace(qq,1);
1570 if(d2pp->flags&PNGD_NO_GAMMA_LABEL)
1571 pngdib_d2p_set_gamma_label(qq,0,0.0);
1572 else
1573 pngdib_d2p_set_gamma_label(qq,1,PNGDIB_DEFAULT_FILE_GAMMA);
1575 pngdib_d2p_set_png_filename(qq,d2pp->pngfn);
1577 if(d2pp->software)
1578 pngdib_d2p_set_software_id(qq, d2pp->software);
1580 if(d2pp->flags&PNGD_DIB_ALPHA32)
1581 pngdib_set_dibalpha32(qq,1);
1583 errcode=pngdib_d2p_run(qq);
1585 if(d2pp->errmsg) {
1586 msg=pngdib_get_error_msg(qq);
1587 lstrcpyn(d2pp->errmsg,msg,100);
1590 pngdib_done(qq);
1591 return errcode;
1594 #endif // PNGDIB_V2COMPATIBLE