Back to index

plt-scheme  4.2.1
wxJPEG.cxx
Go to the documentation of this file.
00001 /*
00002  * This file has both JPEG and PNG support (despite the file name).
00003  *
00004  * The JPEG part Derived from IJG's example.c.
00005  */
00006 
00007 #if defined(_MSC_VER) && defined(MZ_PRECISE_GC)
00008 # include "wx.h"
00009 #endif
00010 #ifdef MZ_PRECISE_GC
00011 # include "common.h"
00012 #endif
00013 
00014 #include "wx_dcmem.h"
00015 #include "wx_gdi.h"
00016 
00017 #include <stdio.h>
00018 #include <stdlib.h>
00019 extern "C" {
00020 # ifdef WX_USE_LIBJPEG
00021 #  include <jpeglib.h>
00022 # else
00023 #  include "jpeg/jpeglib.h"
00024 # endif
00025 # ifdef WX_USE_LIBPNG
00026 #  include <png.h>
00027 # else
00028 #  include "libpng/png.h"
00029 # endif
00030 }
00031 #include <setjmp.h>
00032 
00033 #ifdef MPW_CPLUS
00034 extern "C" { typedef void (*JPEG_ERROR_F_PTR)(j_common_ptr info); }
00035 # define CAST_JPEGP (JPEG_ERROR_F_PTR)
00036 #else
00037 # define CAST_JPEGP /* empty */
00038 #endif
00039 
00040 #ifdef wx_x
00041 # define WX_QUANTIZE 1
00042 # define Q_NOT !
00043 #else
00044 # define WX_QUANTIZE 0
00045 # define Q_NOT /* empty */
00046 #endif
00047 
00048 static wxColor *the_color;
00049 extern void wxmeError(const char *e);
00050 extern int wxGetPreference(const char *name, char *res, long len);
00051 
00052 #ifdef wx_msw
00053 # include "wx_utils.h"
00054 # define wxFOpen(fn, m) _wfopen(wxWIDE_STRING(fn), m)
00055 # define wx_RB_mode L"rb"
00056 # define wx_WB_mode L"wb"
00057 #else
00058 # define wxFOpen(fn, m) fopen(fn, m)
00059 # define wx_RB_mode "rb"
00060 # define wx_WB_mode "wb"
00061 #endif
00062 
00063 #ifndef DRAW_SCANLINE_DEFINED
00064 
00065 static void draw_scanline(JSAMPROW row, int cols, int rownum, int step, JSAMPARRAY colormap, wxMemoryDC *dc,
00066                        int mono)
00067 {
00068   int colnum, r, g, b;
00069 
00070   for (colnum = 0; colnum < cols; colnum++) {
00071 #if WX_QUANTIZE
00072     if (!mono) {
00073       int v;
00074       v = row[colnum];
00075       r = colormap[0][v];
00076       g = colormap[1][v];
00077       b = colormap[2][v];
00078     } else {
00079 #endif
00080       if (step == 1) {
00081        r = row[colnum];
00082        g = row[colnum];
00083        b = row[colnum];
00084       } else {
00085        r = row[colnum * step];
00086        g = row[colnum * step + 1];
00087        b = row[colnum * step + 2];
00088       }
00089 #if WX_QUANTIZE
00090     }
00091 #endif
00092     dc->SetPixelFast(colnum, rownum, r, g, b);
00093   }
00094 }
00095 
00096 #endif
00097 
00098 static void get_scanline(JSAMPROW row, int cols, int rownum, wxMemoryDC *dc)
00099 {
00100   int colnum, d = 0, r, g, b;
00101 
00102   if (!the_color) {
00103     wxREGGLOB(the_color);
00104     the_color = new WXGC_PTRS wxColour(0, 0, 0);
00105   }
00106 
00107   for (colnum = 0; colnum < cols; colnum++, d += 3) {
00108     dc->GetPixel(colnum, rownum, the_color);
00109     r = the_color->Red();
00110     g = the_color->Green();
00111     b = the_color->Blue();
00112     row[d] = r;
00113     row[d+1] = g;
00114     row[d+2] = b;
00115   }
00116 }
00117 
00118 wxMemoryDC *create_dc(int width, int height, wxBitmap *bm, int mono)
00119 {
00120   wxMemoryDC *dc;
00121 
00122   dc = new WXGC_PTRS wxMemoryDC();
00123   if (width >= 0)
00124     bm->Create(width, height, mono ? 1 : -1);
00125   dc->SelectObject(bm);
00126 
00127   if (!dc->Ok()) {
00128     dc->SelectObject(NULL);
00129     return NULL;
00130   }
00131 
00132   return dc;
00133 }
00134 
00135 wxMemoryDC *create_reader_dc(wxBitmap *bm, volatile int *desel)
00136 {
00137   wxMemoryDC *dc;
00138 
00139   dc = new WXGC_PTRS wxMemoryDC(1); /* 1 => read-only */
00140   dc->SelectObject(bm);
00141   if (!dc->GetObject()) {
00142 # ifdef wx_msw
00143     if (bm->selectedInto) {
00144       /* Even selecting into a read-only dc doesn't seem to work
00145         if it already has a dc. Just use that one, then. */
00146       dc = (wxMemoryDC *)bm->selectedInto;
00147       *desel = 0;
00148     } else
00149 # endif
00150       return NULL;
00151   }
00152 
00153   return dc;
00154 }
00155 
00156 /**********************************************************************/
00157 
00158 char jpeg_err_buffer[JMSG_LENGTH_MAX + 256];
00159 
00160 struct my_error_mgr {
00161   struct jpeg_error_mgr pub;       /* "public" fields */
00162   jmp_buf setjmp_buffer;    /* for return to caller */
00163 };
00164 
00165 typedef struct my_error_mgr * my_error_ptr;
00166 
00167 /*
00168  * Here's the routine that will replace the standard error_exit method:
00169  */
00170 
00171 static void my_error_exit(j_common_ptr cinfo)
00172 {
00173   /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
00174   my_error_ptr myerr = (my_error_ptr) cinfo->err;
00175 
00176   (*cinfo->err->format_message)(cinfo, jpeg_err_buffer);
00177 
00178   /* Return control to the setjmp point */
00179   longjmp(myerr->setjmp_buffer, 1);
00180 }
00181 
00182 /*
00183  * Sample routine for JPEG decompression.  We assume that the source file name
00184  * is passed in.  We want to return 1 on success, 0 on error.
00185  */
00186 
00187 int read_JPEG_file(char * filename, wxBitmap *bm)
00188 {
00189   FILE * volatile infile;       /* source file */
00190   JSAMPARRAY buffer;        /* Output row buffer */
00191   int row_stride;           /* physical row width in output buffer */
00192   wxMemoryDC *dc;
00193 #ifdef MZ_PRECISE_GC
00194   START_XFORM_SKIP;
00195 #endif
00196   /* This struct contains the JPEG decompression parameters and pointers to
00197    * working space (which is allocated as needed by the JPEG library).
00198    */
00199   struct jpeg_decompress_struct cinfo;
00200   /* We use our private extension JPEG error handler.
00201    * Note that this struct must live as long as the main JPEG parameter
00202    * struct, to avoid dangling-pointer problems.
00203    */
00204   struct my_error_mgr jerr;
00205 #ifdef MZ_PRECISE_GC
00206   END_XFORM_SKIP;
00207 #endif
00208 
00209   /* In this example we want to open the input file before doing anything else,
00210    * so that the setjmp() error recovery below can assume the file is open.
00211    * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
00212    * requires it in order to read binary files.
00213    */
00214 
00215   if ((infile = wxFOpen(filename, wx_RB_mode)) == NULL) {
00216     sprintf(jpeg_err_buffer, "can't open %.255s\n", filename);
00217     wxmeError(jpeg_err_buffer);
00218     return 0;
00219   }
00220 
00221   /* Step 1: allocate and initialize JPEG decompression object */
00222 
00223   /* We set up the normal JPEG error routines, then override error_exit. */
00224   cinfo.err = jpeg_std_error(&jerr.pub);
00225   jerr.pub.error_exit = CAST_JPEGP my_error_exit;
00226   /* Establish the setjmp return context for my_error_exit to use. */
00227   if (setjmp(jerr.setjmp_buffer)) {
00228     /* If we get here, the JPEG code has signaled an error.
00229      * We need to clean up the JPEG object, close the input file, and return.
00230      */
00231     jpeg_destroy_decompress(&cinfo);
00232     fclose(infile);
00233     wxmeError(jpeg_err_buffer);
00234     return 0;
00235   }
00236   /* Now we can initialize the JPEG decompression object. */
00237   jpeg_create_decompress(&cinfo);
00238 
00239   /* Step 2: specify data destnation (eg, a file) */
00240 
00241   jpeg_stdio_src(&cinfo, infile);
00242 
00243   /* Step 3: read file parameters with jpeg_read_header() */
00244 
00245   (void) jpeg_read_header(&cinfo, TRUE);
00246   /* We can ignore the return value from jpeg_read_header since
00247    *   (a) suspension is not possible with the stdio data source, and
00248    *   (b) we passed TRUE to reject a tables-only JPEG file as an error.
00249    * See libjpeg.doc for more info.
00250    */
00251 
00252   /* Step 4: set parameters for decompression */
00253 #if WX_QUANTIZE
00254   cinfo.quantize_colors = 1;
00255 #endif
00256 
00257   /* Step 5: Start decompressor */
00258 
00259   (void) jpeg_start_decompress(&cinfo);
00260   /* We can ignore the return value since suspension is not possible
00261    * with the stdio data source.
00262    */
00263 
00264   /* We may need to do some setup of our own at this point before reading
00265    * the data.  After jpeg_start_decompress() we have the correct scaled
00266    * output image dimensions available, as well as the output colormap
00267    * if we asked for color quantization.
00268    * In this example, we need to make an output work buffer of the right size.
00269    */ 
00270 
00271   dc = create_dc(cinfo.output_width, cinfo.output_height, bm, 0);
00272   if (!dc) {
00273     /* couldn't allocate DC or select bitmap */
00274     return 0;
00275   }
00276 
00277   /* JSAMPLEs per row in output buffer */
00278   row_stride = cinfo.output_width * cinfo.output_components;
00279   /* Make a one-row-high sample array that will go away when done with image */
00280   buffer = (*cinfo.mem->alloc_sarray)
00281               ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
00282 
00283   /* Step 6: while (scan lines remain to be read) */
00284   /*           jpeg_read_scanlines(...); */
00285 
00286   /* Here we use the library's state variable cinfo.output_scanline as the
00287    * loop counter, so that we don't have to keep track ourselves.
00288    */
00289   dc->BeginSetPixelFast(0, 0, cinfo.output_width, cinfo.output_height);
00290   while (cinfo.output_scanline < cinfo.output_height) {
00291     /* jpeg_read_scanlines expects an array of pointers to scanlines.
00292      * Here the array is only one element long, but you could ask for
00293      * more than one scanline at a time if that's more convenient.
00294      */
00295     (void) jpeg_read_scanlines(&cinfo, buffer, 1);
00296     /* Assume put_scanline_someplace wants a pointer and sample count. */
00297     draw_scanline(buffer[0],
00298                 cinfo.output_width, cinfo.output_scanline - 1, 
00299                 cinfo.output_components, cinfo.colormap,
00300                 dc, cinfo.num_components == 1);
00301   }
00302   dc->EndSetPixelFast();
00303 
00304   /* Step 7: Finish decompression */
00305 
00306   (void) jpeg_finish_decompress(&cinfo);
00307   /* We can ignore the return value since suspension is not possible
00308    * with the stdio data source.
00309    */
00310 
00311   /* Step 8: Release JPEG decompression object */
00312 
00313   /* This is an important step since it will release a good deal of memory. */
00314   jpeg_destroy_decompress(&cinfo);
00315 
00316   /* After finish_decompress, we can close the input file.
00317    * Here we postpone it until after no more JPEG errors are possible,
00318    * so as to simplify the setjmp error logic above.  (Actually, I don't
00319    * think that jpeg_destroy can do an error exit, but why assume anything...)
00320    */
00321   fclose(infile);
00322 
00323   /* At this point you may want to check to see whether any corrupt-data
00324    * warnings occurred (test whether jerr.pub.num_warnings is nonzero).
00325    */
00326 
00327   /* And we're done! */
00328   dc->SelectObject(NULL);
00329   return 1;
00330 }
00331 
00332 int write_JPEG_file(char *filename, wxBitmap *bm, int quality)
00333 {
00334   /* More stuff */
00335   FILE * volatile outfile;         /* target file */
00336   JSAMPROW row_pointer;     /* pointer to JSAMPLE row[s] */
00337   wxMemoryDC * volatile dc;
00338   int wid;
00339   volatile int desel = 1;
00340 
00341 #ifdef MZ_PRECISE_GC
00342   START_XFORM_SKIP;
00343 #endif
00344   /* This struct contains the JPEG compression parameters and pointers to
00345    * working space (which is allocated as needed by the JPEG library).
00346    * It is possible to have several such structures, representing multiple
00347    * compression/decompression processes, in existence at once.  We refer
00348    * to any one struct (and its associated working data) as a "JPEG object".
00349    */
00350   struct jpeg_compress_struct cinfo;
00351   /* This struct represents a JPEG error handler.  It is declared separately
00352    * because applications often want to supply a specialized error handler
00353    * (see the second half of this file for an example).  But here we just
00354    * take the easy way out and use the standard error handler, which will
00355    * print a message on stderr and call exit() if compression fails.
00356    * Note that this struct must live as long as the main JPEG parameter
00357    * struct, to avoid dangling-pointer problems.
00358    */
00359   struct my_error_mgr jerr;
00360 #ifdef MZ_PRECISE_GC
00361   END_XFORM_SKIP;
00362 #endif
00363 
00364   dc = create_reader_dc(bm, (int *)&desel);
00365 
00366   wid = bm->GetWidth();
00367   row_pointer = new WXGC_ATOMIC JSAMPLE[3 * wid];
00368 
00369   if ((outfile = wxFOpen(filename, wx_WB_mode)) == NULL) {
00370     if (desel)
00371       dc->SelectObject(NULL);
00372     sprintf(jpeg_err_buffer, "can't open %.255s\n", filename);
00373     wxmeError(jpeg_err_buffer);
00374     return 0;
00375   }
00376   /* Step 1: allocate and initialize JPEG compression object */
00377 
00378   /* We have to set up the error handler first, in case the initialization
00379    * step fails.  (Unlikely, but it could happen if you are out of memory.)
00380    * This routine fills in the contents of struct jerr, and returns jerr's
00381    * address which we place into the link field in cinfo.
00382    */
00383   /* We set up the normal JPEG error routines, then override error_exit. */
00384   cinfo.err = jpeg_std_error(&jerr.pub);
00385   jerr.pub.error_exit = CAST_JPEGP my_error_exit;
00386   /* Establish the setjmp return context for my_error_exit to use. */
00387   if (setjmp(jerr.setjmp_buffer)) {
00388     /* If we get here, the JPEG code has signaled an error.
00389      * We need to clean up the JPEG object, close the input file, and return.
00390      */
00391     if (desel)
00392       dc->SelectObject(NULL);
00393     jpeg_destroy_compress(&cinfo);
00394     fclose(outfile);
00395     wxmeError(jpeg_err_buffer);
00396     return 0;
00397   }
00398 
00399   /* Now we can initialize the JPEG compression object. */
00400   jpeg_create_compress(&cinfo);
00401 
00402   /* Step 2: specify data destination (eg, a file) */
00403   /* Note: steps 2 and 3 can be done in either order. */
00404 
00405   /* Here we use the library-supplied code to send compressed data to a
00406    * stdio stream.  You can also write your own code to do something else. */
00407   jpeg_stdio_dest(&cinfo, outfile);
00408 
00409   /* Step 3: set parameters for compression */
00410 
00411   /* First we supply a description of the input image.
00412    * Four fields of the cinfo struct must be filled in:
00413    */
00414   cinfo.image_width = wid;  /* image width and height, in pixels */
00415   cinfo.image_height = bm->GetHeight();
00416   cinfo.input_components = 3;             /* # of color components per pixel */
00417   cinfo.in_color_space = JCS_RGB;  /* colorspace of input image */
00418   /* Now use the library's routine to set default compression parameters.
00419    * (You must set at least cinfo.in_color_space before calling this,
00420    * since the defaults depend on the source color space.)
00421    */
00422   jpeg_set_defaults(&cinfo);
00423   /* Now you can set any non-default parameters you wish to.
00424    * Here we just illustrate the use of quality (quantization table) scaling:
00425    */
00426   jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
00427 
00428   /* Step 4: Start compressor */
00429 
00430   /* TRUE ensures that we will write a complete interchange-JPEG file.
00431    * Pass TRUE unless you are very sure of what you're doing.
00432    */
00433   jpeg_start_compress(&cinfo, TRUE);
00434 
00435   /* Step 5: while (scan lines remain to be written) */
00436   /*           jpeg_write_scanlines(...); */
00437 
00438   /* Here we use the library's state variable cinfo.next_scanline as the
00439    * loop counter, so that we don't have to keep track ourselves.
00440    * To keep things simple, we pass one scanline per call; you can pass
00441    * more if you wish, though.
00442    */
00443 
00444   while (cinfo.next_scanline < cinfo.image_height) {
00445     /* jpeg_write_scanlines expects an array of pointers to scanlines.
00446      * Here the array is only one element long, but you could pass
00447      * more than one scanline at a time if that's more convenient.
00448      */
00449     get_scanline(row_pointer, wid, cinfo.next_scanline, dc);
00450     (void)jpeg_write_scanlines(&cinfo, &row_pointer, 1);
00451   }
00452 
00453   /* Step 6: Finish compression */
00454 
00455   jpeg_finish_compress(&cinfo);
00456   /* After finish_compress, we can close the output file. */
00457   fclose(outfile);
00458 
00459   /* Step 7: release JPEG compression object */
00460 
00461   /* This is an important step since it will release a good deal of memory. */
00462   jpeg_destroy_compress(&cinfo);
00463 
00464   if (desel)
00465     dc->SelectObject(NULL);
00466   
00467   /* And we're done! */
00468   return 1;
00469 }
00470 
00471 /*
00472  * SOME FINE POINTS:
00473  *
00474  * In the above code, we ignored the return value of jpeg_read_scanlines,
00475  * which is the number of scanlines actually read.  We could get away with
00476  * this because we asked for only one line at a time and we weren't using
00477  * a suspending data source.  See libjpeg.doc for more info.
00478  *
00479  * We cheated a bit by calling alloc_sarray() after jpeg_start_decompress();
00480  * we should have done it beforehand to ensure that the space would be
00481  * counted against the JPEG max_memory setting.  In some systems the above
00482  * code would risk an out-of-memory error.  However, in general we don't
00483  * know the output image dimensions before jpeg_start_decompress(), unless we
00484  * call jpeg_calc_output_dimensions().  See libjpeg.doc for more about this.
00485  *
00486  * Scanlines are returned in the same order as they appear in the JPEG file,
00487  * which is standardly top-to-bottom.  If you must emit data bottom-to-top,
00488  * you can use one of the virtual arrays provided by the JPEG memory manager
00489  * to invert the data.  See wrbmp.c for an example.
00490  *
00491  * As with compression, some operating modes may require temporary files.
00492  * On some systems you may need to set up a signal handler to ensure that
00493  * temporary files are deleted if the program is interrupted.  See libjpeg.doc.
00494  */
00495 
00496 /**********************************************************************/
00497 
00498 static char *png_err_msg;
00499 static int pem_registered;
00500 
00501 #ifdef MPW_CPLUS
00502 extern "C" {
00503   typedef void (*UEP_PTR)(png_structp png_ptr, png_const_charp msg);
00504   typedef void (*UWP_PTR)(png_structp png_ptr, png_const_charp msg);
00505 }
00506 # define CAST_UEP (UEP_PTR)
00507 # define CAST_UWP (UWP_PTR)
00508 #else
00509 # define CAST_UEP /* empty */
00510 # define CAST_UWP /* empty */
00511 #endif
00512 
00513 static void user_error_proc(png_structp png_ptr, png_const_charp msg)
00514 {
00515   int len;
00516 
00517   if (!pem_registered) {
00518     wxREGGLOB(png_err_msg);
00519   }
00520   len = strlen(msg);
00521   png_err_msg = new WXGC_ATOMIC char[len + 1];
00522   memcpy(png_err_msg, msg, len + 1);
00523   
00524   longjmp(png_ptr->jmpbuf, 1);
00525 }
00526 
00527 static void user_warn_proc(png_structp info, png_const_charp msg)
00528 {
00529 }
00530 
00531 static void png_start_lines(wxMemoryDC *dc, wxMemoryDC *mdc, int width, int height)
00532 {
00533   dc->BeginSetPixelFast(0, 0, width, height);
00534   if (mdc)
00535     mdc->BeginSetPixelFast(0, 0, width, height);
00536 }
00537 
00538 static void png_end_lines(wxMemoryDC *dc, wxMemoryDC *mdc)
00539 {
00540   dc->EndSetPixelFast();
00541   if (mdc)
00542     mdc->EndSetPixelFast();
00543 }
00544 
00545 static void png_draw_line(png_bytep row, int cols, int rownum, wxMemoryDC *dc, wxMemoryDC *mdc, int step)
00546 {
00547   int colnum, delta;
00548 
00549   for (colnum = 0, delta = 0; colnum < cols; colnum++, delta += step) {
00550     dc->SetPixelFast(colnum, rownum,
00551                    row[delta], 
00552                    row[delta + 1], 
00553                    row[delta + 2]);
00554     if (mdc) {
00555       mdc->SetPixelFast(colnum, rownum,
00556                      row[delta + 3],
00557                      row[delta + 3],
00558                      row[delta + 3]);
00559     }
00560   }
00561 }
00562 
00563 static void png_draw_line1(png_bytep row, int cols, int rownum, wxMemoryDC *dc)
00564 {
00565   int colnum, delta = 0, bit;
00566 
00567   for (colnum = 0; colnum < cols; delta++) {
00568     for (bit = 128; (colnum < cols) && bit; colnum++, bit = bit >> 1) {
00569       if (row[delta] & bit)
00570        dc->SetPixelFast(colnum, rownum, 255, 255, 255);
00571       else
00572        dc->SetPixelFast(colnum, rownum, 0, 0, 0);
00573     }
00574   }
00575 }
00576 
00577 static void png_get_line(png_bytep row, int cols, int rownum, wxMemoryDC *dc, wxMemoryDC *mdc)
00578 {
00579   int colnum, delta, r, g, b;
00580   int step = (mdc ? 4 : 3);
00581 
00582   if (!the_color) {
00583     wxREGGLOB(the_color);
00584     the_color = new WXGC_PTRS wxColour(0, 0, 0);
00585   }
00586 
00587   for (colnum = 0, delta = 0; colnum < cols; colnum++, delta += step) {
00588     dc->GetPixel(colnum, rownum, the_color);
00589     r = the_color->Red();
00590     g = the_color->Green();
00591     b = the_color->Blue();
00592     row[delta] = r;
00593     row[delta+1] = g;
00594     row[delta+2] = b;
00595     if (mdc) {
00596       mdc->GetPixel(colnum, rownum, the_color);
00597       r = the_color->Red();
00598       row[delta+3] = r;
00599     }
00600   }
00601 }
00602 
00603 static void png_get_line1(png_bytep row, int cols, int rownum, wxMemoryDC *dc)
00604 {
00605   int colnum, delta, bit, r, g, b, bits;
00606 
00607   if (!the_color) {
00608     wxREGGLOB(the_color);
00609     the_color = new WXGC_PTRS wxColour(0, 0, 0);
00610   }
00611 
00612   for (colnum = 0, delta = 0; colnum < cols; delta++) {
00613     bits = 0;
00614     for (bit = 128; (colnum < cols) && bit; colnum++, bit = bit >> 1) {
00615       dc->GetPixel(colnum, rownum, the_color);
00616       r = the_color->Red();
00617       g = the_color->Green();
00618       b = the_color->Blue();
00619       if ((r == 255) && (g == 255) && (b == 255)) 
00620        bits |= bit;
00621     }
00622     row[delta] = bits;
00623   }
00624 }
00625 
00626 /**********************************************************************/
00627 
00628 int wx_read_png(char *file_name, wxBitmap *bm, int w_mask, wxColour *bg)
00629 {
00630    png_structp png_ptr;
00631    png_structp volatile png_ptr_orig;
00632    png_infop info_ptr;
00633    png_infop volatile info_ptr_orig;
00634    png_uint_32 width, height;
00635    int bit_depth, color_type, interlace_type, is_mono = 0, row_width;
00636    unsigned int number_passes, pass, y;
00637    FILE * volatile fp;
00638    png_bytep *rows, row;
00639    wxMemoryDC * volatile dc = NULL;
00640    wxMemoryDC *mdc = NULL;
00641    wxBitmap *mbm = NULL;
00642 
00643    if ((fp = wxFOpen(file_name, wx_RB_mode)) == NULL)
00644      return 0;
00645 
00646    /* Create and initialize the png_struct with the desired error handler
00647     * functions.  If you want to use the default stderr and longjump method,
00648     * you can supply NULL for the last three parameters.  We also supply the
00649     * the compiler header file version, so that we know if the application
00650     * was compiled with a compatible version of the library.  REQUIRED
00651     */
00652    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, 
00653                                 CAST_UEP user_error_proc, 
00654                                 CAST_UWP user_warn_proc);
00655 
00656    if (png_ptr == NULL)
00657    {
00658       fclose(fp);
00659       return 0;
00660    }
00661 
00662    /* Allocate/initialize the memory for image information.  REQUIRED. */
00663    info_ptr = png_create_info_struct(png_ptr);
00664    if (info_ptr == NULL)
00665    {
00666       fclose(fp);
00667       png_destroy_read_struct(&png_ptr, NULL, NULL);
00668       return 0;
00669    }
00670 
00671    /* Set error handling if you are using the setjmp/longjmp method (this is
00672     * the normal method of doing things with libpng).  REQUIRED unless you
00673     * set up your own error handlers in the png_create_read_struct() earlier.
00674     */
00675 
00676    png_ptr_orig = png_ptr;
00677    info_ptr_orig = info_ptr;
00678 
00679    if (setjmp(png_ptr->jmpbuf))
00680    {
00681      /* Free all of the memory associated with the png_ptr and info_ptr */
00682      png_ptr = png_ptr_orig;
00683      info_ptr = info_ptr_orig;
00684      png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
00685      fclose(fp);
00686      if (dc)
00687        dc->SelectObject(NULL);
00688      /* If we get here, we had a problem reading the file */
00689      return 0;
00690    }
00691 
00692    /* Set up the input control if you are using standard C streams */
00693    png_init_io(png_ptr, fp);
00694 
00695    /* The call to png_read_info() gives us all of the information from the
00696     * PNG file before the first IDAT (image data chunk).  REQUIRED
00697     */
00698    png_read_info(png_ptr, info_ptr);
00699 
00700    png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
00701               &interlace_type, NULL, NULL);
00702 
00703    if (w_mask) {
00704      /* Is the mask actually useful? */
00705      if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)
00706         && !(color_type & PNG_COLOR_MASK_ALPHA))
00707        w_mask = 0;
00708    }
00709 
00710    if ((bit_depth == 1)
00711        && (color_type == PNG_COLOR_TYPE_GRAY)
00712        && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
00713      /* Special handling for monochrome so that we don't use 32x
00714        the necessary memory. */
00715      is_mono = 1;
00716    } else {
00717      /* Normalize formal of returned rows: */
00718      if (color_type == PNG_COLOR_TYPE_PALETTE)
00719        png_set_palette_to_rgb(png_ptr);
00720      if (color_type == PNG_COLOR_TYPE_GRAY ||
00721         color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
00722        png_set_gray_to_rgb(png_ptr);
00723      if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
00724        png_set_tRNS_to_alpha(png_ptr);
00725      if (bit_depth == 16)
00726        png_set_strip_16(png_ptr);
00727 
00728      /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
00729      png_set_gray_1_2_4_to_8(png_ptr);
00730    }
00731 
00732    /* Set the background color to draw transparent and alpha images over.
00733     * It is possible to set the red, green, and blue components directly
00734     * for paletted images instead of supplying a palette index.  Note that
00735     * even if the PNG file supplies a background, you are not required to
00736     * use it - you should use the (solid) application background if it has one.
00737     */
00738    if (!w_mask && !is_mono) {
00739      png_color_16 *image_background;
00740 
00741      if (!bg && png_get_bKGD(png_ptr, info_ptr, &image_background))
00742        png_set_background(png_ptr, image_background,
00743                        PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
00744      else {
00745        png_color_16 my_background;
00746        
00747        if (bg) {
00748         int g;
00749         my_background.red = bg->Red();
00750         my_background.green = bg->Green();
00751         my_background.blue = bg->Blue();
00752         g = (((int)my_background.red) 
00753              + ((int)my_background.green)
00754              + ((int)my_background.blue)) / 3;
00755         my_background.gray = g;
00756        } else {
00757         my_background.red = 0xff;
00758         my_background.green = 0xff;
00759         my_background.blue = 0xff;
00760         my_background.gray = 0xff;
00761        }
00762 
00763        if (bit_depth == 16) {
00764         my_background.red = (my_background.red << 8) | my_background.red;
00765         my_background.green = (my_background.green << 8) | my_background.green;
00766         my_background.blue = (my_background.blue << 8) | my_background.blue;
00767         my_background.gray = (my_background.gray << 8) | my_background.gray;
00768        }
00769 
00770        png_set_background(png_ptr, &my_background,
00771                        PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
00772      }
00773    }
00774 
00775    /* Gamma correction --- only if the file has a gamma.
00776       This gamma correction messes with the ability of
00777       PNGs to keep exact RGB information, for the many
00778       cases where that could make sense. So no gamma
00779       data in the file means we won't try to correct it. */
00780    {
00781      double gamma;
00782 
00783      if (png_get_gAMA(png_ptr, info_ptr, &gamma)) {
00784        double screen_gamma;
00785        char *gamma_str;
00786        char buf[30];
00787        
00788        if (wxGetPreference("gamma", buf, 30)) {
00789         screen_gamma = (double)atof(buf);
00790        } else if ((gamma_str = getenv("SCREEN_GAMMA"))) {
00791         screen_gamma = (double)atof(gamma_str);
00792        } else
00793         screen_gamma = 0;
00794        
00795        if (!(screen_gamma > 0.0) || !(screen_gamma < 10.0)) {
00796         /* Guess */
00797 #ifdef wx_mac
00798         screen_gamma = 1.7;  /* A good guess for Mac systems */
00799 #else
00800         screen_gamma = 2.0; /* A good guess for a PC monitor */
00801 #endif
00802        }
00803 
00804        png_set_gamma(png_ptr, screen_gamma, gamma);
00805      }
00806    }
00807 
00808    if (w_mask && !is_mono) {
00809      /* Add filler (or alpha) byte (before/after each RGB triplet) */
00810      /* Actually, invert so that it's a mask. */
00811      png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
00812      png_set_invert_alpha(png_ptr);
00813    }
00814 
00815    /* Turn on interlace handling.  REQUIRED if you are not using
00816     * png_read_image().  To see how to handle interlacing passes,
00817     * see the png_read_row() method below:
00818     */
00819    number_passes = png_set_interlace_handling(png_ptr);
00820 
00821    /* Optional call to gamma correct and add the background to the palette
00822     * and update info structure.  REQUIRED if you are expecting libpng to
00823     * update the palette for you (ie you selected such a transform above).
00824     */
00825    png_read_update_info(png_ptr, info_ptr);
00826 
00827    /* Allocate the memory to hold the image using the fields of info_ptr. */
00828 
00829 #ifdef MZ_PRECISE_GC
00830    rows = (png_bytep *)GC_malloc(sizeof(png_bytep) * height);
00831 #else
00832    rows = new WXGC_PTRS png_bytep[height];
00833 #endif
00834 
00835    row_width = png_get_rowbytes(png_ptr, info_ptr);
00836    for (y = 0; y < height; y++) {
00837      row = new WXGC_ATOMIC png_byte[row_width];
00838      rows[y] = row;
00839    }
00840 
00841    dc = create_dc(width, height, bm, is_mono);
00842    if (!dc) {
00843      if (dc)
00844        dc->SelectObject(NULL);
00845      png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
00846      fclose(fp);
00847      return 0;
00848    }
00849 
00850    for (pass = 0; pass < number_passes; pass++) {
00851      png_read_rows(png_ptr, rows, NULL, height);
00852    }
00853 
00854    if (is_mono) {
00855      png_start_lines(dc, mdc, width, height);
00856      for (y = 0; y < height; y++) {
00857        png_draw_line1(rows[y], width, y, dc);
00858      }
00859      png_end_lines(dc, mdc);
00860    } else {
00861      if (w_mask) {
00862        int mono_mask;
00863        unsigned int x;
00864 
00865        /* Will a monochrome mask do? */
00866        for (y = 0; y < height; y++) {
00867         row = rows[y];
00868         for (x = 0; x < width; x++) {
00869           int val;
00870           val = row[(x * 4) + 3];
00871           if ((val != 0) && (val != 255))
00872             break;
00873         }
00874         if (x < width)
00875           break;
00876        } 
00877 
00878        mono_mask = ((y < height) ? 0 : 1);
00879 
00880        mbm = new WXGC_PTRS wxBitmap(width, height, mono_mask);
00881        if (mbm->Ok())
00882         mdc = create_dc(-1, -1, mbm, mono_mask);
00883        else
00884         mdc = NULL;
00885      }
00886 
00887      png_start_lines(dc, mdc, width, height);
00888      for (y = 0; y < height; y++) {
00889        png_draw_line(rows[y], width, y, dc, mdc, w_mask ? 4 : 3);
00890      }
00891      png_end_lines(dc, mdc);
00892    }
00893 
00894    /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
00895    png_read_end(png_ptr, info_ptr);
00896 
00897    /* At this point you have read the entire image */
00898 
00899    /* clean up after the read, and free any memory allocated - REQUIRED */
00900    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
00901 
00902    /* close the file */
00903    fclose(fp);
00904 
00905    dc->SelectObject(NULL);
00906    if (mdc) {
00907      mdc->SelectObject(NULL);
00908      bm->SetMask(mbm);
00909    }
00910 
00911    /* that's it */
00912    return 1;
00913 }
00914 
00915 int wx_write_png(char *file_name, wxBitmap *bm)
00916 {
00917    png_structp png_ptr;
00918    png_structp volatile png_ptr_orig;
00919    png_infop info_ptr;
00920    png_infop volatile info_ptr_orig;
00921    int width, height;
00922    int bit_depth, color_type, row_width;
00923    int y;
00924    FILE *volatile fp;
00925    png_bytep *rows, row;
00926    wxMemoryDC * volatile dc = NULL;
00927    wxMemoryDC * volatile mdc = NULL;
00928    wxBitmap *mbm = NULL;
00929    volatile int desel = 1;
00930    volatile int mdesel = 1;
00931 
00932    if ((fp = wxFOpen(file_name, wx_WB_mode)) == NULL)
00933      return 0;
00934 
00935    /* Create and initialize the png_struct with the desired error handler
00936     * functions.  If you want to use the default stderr and longjump method,
00937     * you can supply NULL for the last three parameters.  We also supply the
00938     * the compiler header file version, so that we know if the application
00939     * was compiled with a compatible version of the library.  REQUIRED
00940     */
00941    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, 
00942                                  CAST_UEP user_error_proc, 
00943                                  CAST_UWP user_warn_proc);
00944 
00945    if (png_ptr == NULL)
00946    {
00947       fclose(fp);
00948       return 0;
00949    }
00950 
00951    /* Allocate/initialize the memory for image information.  REQUIRED. */
00952    info_ptr = png_create_info_struct(png_ptr);
00953    if (info_ptr == NULL)
00954    {
00955       fclose(fp);
00956       png_destroy_write_struct(&png_ptr, NULL);
00957       return 0;
00958    }
00959 
00960    /* Set error handling if you are using the setjmp/longjmp method (this is
00961     * the normal method of doing things with libpng).  REQUIRED unless you
00962     * set up your own error handlers in the png_create_read_struct() earlier.
00963     */
00964 
00965    png_ptr_orig = png_ptr;
00966    info_ptr_orig = info_ptr;
00967    if (setjmp(png_ptr->jmpbuf)) {
00968      /* Free all of the memory associated with the png_ptr and info_ptr */
00969      png_ptr = png_ptr_orig;
00970      info_ptr = info_ptr_orig;
00971      png_destroy_write_struct(&png_ptr, &info_ptr);
00972      fclose(fp);
00973      if (dc && desel)
00974        dc->SelectObject(NULL);
00975      if (mdc && mdesel)
00976        mdc->SelectObject(NULL);
00977      /* If we get here, we had a problem reading the file */
00978      return 0;
00979    }
00980 
00981    /* Set up the input control if you are using standard C streams */
00982    png_init_io(png_ptr, fp);
00983 
00984    width = bm->GetWidth();
00985    height = bm->GetHeight();
00986    bit_depth = 8;
00987    
00988    mbm = bm->GetMask();
00989    if (mbm && mbm->Ok() && (mbm->GetWidth() == width) &&  (mbm->GetHeight() == height))
00990      color_type = PNG_COLOR_TYPE_RGB_ALPHA;
00991    else {
00992      color_type = PNG_COLOR_TYPE_RGB;
00993      mbm = NULL;
00994    }
00995 
00996    if ((bm->GetDepth() == 1) && !mbm) {
00997      bit_depth = 1;
00998      color_type = PNG_COLOR_TYPE_GRAY;
00999    }
01000 
01001    /* Set the image information here.  Width and height are up to 2^31,
01002     * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
01003     * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
01004     * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
01005     * or PNG_COLOR_TYPE_RGB_ALPHA.  interlace is either PNG_INTERLACE_NONE or
01006     * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
01007     * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
01008     */
01009    png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type,
01010               PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, 
01011               PNG_FILTER_TYPE_DEFAULT);
01012 
01013    if (mbm)
01014      png_set_invert_alpha(png_ptr);
01015 
01016    /* Write the file header information.  REQUIRED */
01017    png_write_info(png_ptr, info_ptr);
01018 
01019    /* Allocate the memory to hold the image using the fields of info_ptr. */
01020 #ifdef MZ_PRECISE_GC
01021    rows = (png_bytep *)GC_malloc(sizeof(png_bytep) * height);
01022 #else
01023    rows = new WXGC_PTRS png_bytep[height];
01024 #endif
01025    row_width = png_get_rowbytes(png_ptr, info_ptr);
01026    for (y = 0; y < height; y++) {
01027      row = new WXGC_ATOMIC png_byte[row_width];
01028      rows[y] = row;
01029    }
01030 
01031    dc = create_reader_dc(bm, &desel);
01032    if (mbm)
01033      mdc = create_reader_dc(mbm, &mdesel);
01034    else
01035      mdc = NULL;
01036 
01037    if (bit_depth == 1) {
01038      for (y = 0; y < height; y++) {
01039        png_get_line1(rows[y], width, y, dc);
01040      }
01041    } else {
01042      for (y = 0; y < height; y++) {
01043        png_get_line(rows[y], width, y, dc, mdc);
01044      }
01045    }
01046 
01047    png_write_image(png_ptr, rows);
01048 
01049    png_write_end(png_ptr, info_ptr);
01050 
01051    /* clean up after the write, and free any memory allocated */
01052    png_destroy_write_struct(&png_ptr, &info_ptr);
01053 
01054    /* close the file */
01055    fclose(fp);
01056 
01057    if (desel)
01058      dc->SelectObject(NULL);
01059    if (mdc && mdesel) {
01060      mdc->SelectObject(NULL);
01061    }
01062 
01063    /* that's it */
01064    return 1;
01065 }