Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

image.C

Go to the documentation of this file.
00001 /**********************************************************************
00002  * image.H
00003  **********************************************************************/
00004 
00005 #include "std/config.H"
00006 #include "geom/image.H"
00007 #include "geom/rgba.H"
00008 static
00009 inline
00010 void
00011 eat_whitespace(istream& in)
00012 {
00013    while (isspace(in.peek())) {
00014       in.get();
00015    }
00016 }
00017 
00018 /***********************************************************************
00019  * Method : Image::copy
00020  * Params :
00021  * Returns: uchar* (copy of image data)
00022  * Effects: allocates an array, copies image data into it
00023  ***********************************************************************/
00024 uchar*
00025 Image::copy()
00026 {
00027    if (empty())
00028       return 0;
00029 
00030    uchar* ret = new uchar [ size() ];
00031 
00032    if (ret)     memcpy(ret, _data, size());
00033    else         err_ret("Image::copy: can't allocate data");
00034 
00035    return ret;
00036 }
00037 
00038 int
00039 Image::resize_rows_mult_4()
00040 {
00041    if (empty() || (row_size()%4) == 0)
00042       return 1;
00043 
00044    uint   new_w = _width + 4 - _width%4;
00045    uchar* new_d = 0;
00046    if ((new_d = new uchar [ new_w * _height * _bpp ]) == 0) {
00047       err_ret("Image::resize_rows_mult_4: can't allocate data");
00048       return 0;
00049    }
00050    int new_row_size = new_w*_bpp;
00051    for (uint r=0; r<_height; r++)
00052       memcpy(new_d + new_row_size*r, row(r), row_size());
00053 
00054    set(new_w, _height, _bpp, new_d, 0);
00055 
00056    return 1;
00057 }
00058 
00059 int 
00060 Image::copy_tile(const Image& tile, uint i, uint j)
00061 {
00062    // scenario:
00063    //   this is a big image.
00064    //   tile is small enough that lots of copies of 
00065    //   it could be arranged to "tile" into this one.
00066    //   we'll copy data from tile into a slot of this image. 
00067    //   the slot is over i and up j copies of the tile image.
00068    //
00069    // how come why:
00070    //   used to perform hi-res screen grabs.
00071    //   the screen is rendered in separate tiles which
00072    //   are then arranged into a single hi-res image.
00073 
00074    // precondition: both images have the same bytes
00075    //   per pixel, and the designated slot fits in
00076    //   this image.
00077 
00078    if (tile._width*(i+1) > _width ||
00079        tile._height*(j+1) > _height ||
00080        tile._bpp != _bpp) {
00081       err_msg("Image::copy_tile: bad size in tiled image");
00082       return 0;
00083    }
00084 
00085    // copy each row into correct slot in this image:
00086    int row_offset = tile.row_size()*i;
00087    for (uint r=0; r<tile._height; r++) {
00088       memcpy(row(tile._height*j + r) + row_offset,      // destination
00089              tile.row(r),                               // source
00090              tile.row_size());                          // number of bytes
00091    }
00092 
00093    return 1;
00094 }
00095 
00096 /***********************************************************************
00097  * Method : Image::load_file
00098  * Params : char* file
00099  * Returns: int (success/failure)
00100  * Effects: tries to load data from file
00101  ***********************************************************************/
00102 int
00103 Image::load_file(char *file)
00104 {
00105    if (!file || *file == '\0')
00106       return 0;
00107 
00108    // see if file can be opened before running off parsing...
00109    {
00110 #if (defined (WIN32) && defined(_MSC_VER) && (_MSC_VER <=1300)) /*VS 6.0*/
00111       ifstream in(file, ios::in | ios::nocreate );
00112 #else
00113       ifstream in(file);
00114 #endif
00115       if (!in.good()) 
00116       {
00117          err_mesg(ERR_LEV_INFO | ERR_INCL_ERRNO, "Image::load_file() - ERROR! Can't open file %s", file);
00118          return 0;
00119       }
00120    }
00121 
00122    // try to read it as a png file
00123    if (read_png(file))
00124       return 1;
00125 
00126    // try to read it as a pnm file:
00127    if (read_pnm(file))
00128       return 1;
00129 
00130    // add other formats here...
00131    return 0;
00132 }
00133 
00134 int
00135 Image::read_pnm(char* file)
00136 {
00137 #if (defined (WIN32) && defined(_MSC_VER) && (_MSC_VER <=1200)) /*VS 6.0*/
00138    ifstream in(file, ios::in | ios::binary | ios::nocreate );
00139 #else
00140    ifstream in(file);
00141 #endif
00142 
00143    if (!in.good()) 
00144    {
00145       err_ret("Image::read_pnm() - ERROR! Can't open file %s", file);
00146       return 0;
00147    }
00148 
00149    char head[128];
00150    in >> head;
00151 
00152    if      (!strcmp(head, "P2"))        return read_pgm(in, 1);
00153    else if (!strcmp(head, "P5"))        return read_pgm(in, 0);
00154    else if (!strcmp(head, "P3"))        return read_ppm(in, 1);
00155    else if (!strcmp(head, "P6"))        return read_ppm(in, 0);
00156    else                                 return 0;
00157 }
00158 
00159 inline int
00160 pnm_read_int(istream& in, uint* d)
00161 {
00162    while (!in.eof() && !in.bad()) {
00163       char buf[128];
00164       in >> buf;
00165       if (buf[0] == '#')
00166          in.getline(buf, 128);
00167       else if (sscanf(buf, "%d", d) == 1)
00168          return 1;
00169    }
00170    return 0;
00171 }
00172 
00173 
00174 int
00175 Image::read_pgm(istream& in, bool ascii)
00176 {
00177    // reset and free up memory:
00178    clear();
00179 
00180    // read dimensions (skipping comments)
00181    pnm_read_int(in, &_width);
00182    pnm_read_int(in, &_height);
00183    uint max_val;
00184    pnm_read_int(in, &max_val);
00185    if (!ascii) eat_whitespace(in);
00186 
00187    // Some machines can't handle luminance (greyscale) textures,
00188    // so we load the greyscale image into RGB triplets:
00189    _bpp = 3;
00190 
00191    if (in.bad() || in.eof()) {
00192       err_ret("Image::read_pgm: error reading stream");
00193       clear();
00194       return 0;
00195    } else if ((_data = new uchar [ _width*_height*_bpp ]) == 0) {
00196       err_ret("Image::read_pgm: can't allocate data");
00197       clear();
00198       return 0;
00199    }
00200    _no_delete = 0;
00201 
00202    // read the image (invert vertically):
00203    int row_bytes = _width*_bpp, val;
00204    for (int y=_height-1; y>=0; y--) {
00205       uchar* row = _data + y*row_bytes;
00206       for (unsigned int x=0; x<_width; x++) {
00207 
00208          if (in.bad() || in.eof()) {
00209             err_ret("Image::read_pgm: error reading stream");
00210             clear();
00211             return 0;
00212          }
00213 
00214          if (ascii)     in >> val;
00215          else           val = in.get();
00216 
00217          *row++ = val;
00218          *row++ = val;
00219          *row++ = val;
00220       }
00221    }
00222 
00223    return 1;
00224 }
00225 
00226 /***********************************************************************
00227  * Method : Image::read_ppm
00228  * Params : istream& in, bool ascii
00229  * Returns: int (success/failure)
00230  * Effects: reads ppm (pixmap) file into memory
00231  ***********************************************************************/
00232 int
00233 Image::read_ppm(istream& in, bool ascii)
00234 {
00235    // reset and free up memory:
00236    clear();
00237 
00238    // read dimensions (skipping comments)
00239    pnm_read_int(in, &_width);
00240    pnm_read_int(in, &_height);
00241    uint max_val;
00242    pnm_read_int(in, &max_val);
00243    if (!ascii) eat_whitespace(in);
00244 
00245    _bpp = 3;
00246 
00247    if (in.bad() || in.eof()) {
00248       err_ret("Image::read_ppm: error reading from stream");
00249       clear();
00250       return 0;
00251    } else if ((_data = new uchar [ _width*_height*_bpp ]) == 0) {
00252       err_ret("Image::read_ppm: can't allocate data");
00253       clear();
00254       return 0;
00255    }
00256    _no_delete = 0;
00257 
00258    // read the image (invert vertically):
00259    int row_bytes = _width*_bpp, val;
00260    for (int y=_height-1; y>=0; y--) {
00261       uchar* row = _data + y*row_bytes;
00262 
00263       if (in.bad() || in.eof()) {
00264          err_ret("Image::read_ppm: error reading from stream");
00265          clear();
00266          return 0;
00267       }
00268 
00269       if (!ascii) {
00270          in.read((char *) row, _width*3);
00271       } else {
00272          for (unsigned int x=0; x<_width; x++) {
00273             in >> val; *row++ = val;
00274             in >> val; *row++ = val;
00275             in >> val; *row++ = val;
00276          }
00277       }
00278    }
00279 
00280    return 1;
00281 }
00282 
00283 /***********************************************************************
00284  * Method : Image::write_pnm
00285  * Params : char* file_name
00286  * Returns: int (success/failure)
00287  * Effects: writes pnm file to disk (not implemented)
00288  ***********************************************************************/
00289 int
00290 Image::write_pnm(char*file) const
00291 {
00292    if (_bpp != 3) {
00293      err_msg("Image::write_pnm: writing files w/o 3 BPP is unimplemented");
00294      return 0;
00295    }
00296 
00297    cerr << "WARNING: writing ppm file, but these colors appear to be off from" << endl;
00298    cerr << "         the ones shown in the JOT window.  Somehow there's error" << endl;
00299    cerr << "         being introduced, it appears.  This needs debugging."     << endl;
00300 
00301    FILE* fp;
00302    fp = fopen(file, "wb");
00303 
00304    // write ppm RGB header
00305    fprintf(fp, "P6\n");
00306 
00307    // read dimensions (skipping comments)
00308    fprintf(fp, "%d %d\n", _width, _height);
00309    fprintf(fp, "255\n"); // (max_val always seems to be 255)
00310 
00311    // write data
00312    int row_bytes = _width*_bpp;
00313    for(int y=_height-1;y>=0;y--)
00314      fwrite(_data + y * row_bytes, _bpp, _width, fp);
00315 
00316    fclose(fp);
00317 
00318    return 1;
00319 }
00320 
00321 /***********************************************************************
00322  * Method : Image::expand_power2
00323  * Params :
00324  * Returns: bool (true if anything changed)
00325  * Effects: resizes image so width and height are powers of 2.
00326  *          pixels outside old dimensions are assigned 0 value
00327  ***********************************************************************/
00328 bool
00329 Image::expand_power2()
00330 {
00331    if (empty())
00332       return false;
00333 
00334    unsigned int w = getPower2Size(_width);
00335    unsigned int h = getPower2Size(_height);
00336    if (w == _width && h == _height)
00337       return false;
00338 
00339    uchar *data;
00340    if ((data = new uchar [ (w*h*_bpp) ]) == 0) {
00341       err_ret("Image::expand_power2: can't allocate data");
00342       return false;
00343    }
00344    memset(data, 0, w*h*_bpp);
00345    int old_row_size = _width * _bpp;
00346    int new_row_size = w * _bpp;
00347    for (uint y=0; y<_height; y++)
00348       memcpy(data  + y*new_row_size,
00349              _data + y*old_row_size,
00350              old_row_size);
00351    set(w,h,_bpp,data,0);
00352    return true;
00353 }
00354 
00355 #include "libpng/png.h"
00356 
00357 /***********************************************************************
00358  * Method : Image::open_png
00359  * Params : char *file_name
00360  * Returns: FILE* (null on failure)
00361  * Effects: opens given file and verifies it is a png file
00362  ***********************************************************************/
00363 FILE*
00364 Image::open_png(char *file_name)
00365 {
00366    // open the file and verify it is a PNG file:
00367    // Windows needs the following to be "rb", the b turns off
00368    // translating from DOS text to UNIX text
00369    FILE* fp = fopen(file_name, "rb");
00370    if (!fp) {
00371       err_ret("Image::open_png() - ERROR! Can't open file %s", file_name);
00372       return 0;
00373    }
00374 
00375    // Read in the signature bytes:
00376    unsigned char buf[PNG_BYTES_TO_CHECK];
00377    if (fread(buf, 1, PNG_BYTES_TO_CHECK, fp) != PNG_BYTES_TO_CHECK) {
00378       err_ret("Image::open_png: can't read file %s", file_name);
00379       return 0;
00380    }
00381 
00382    // Compare the first PNG_BYTES_TO_CHECK bytes of the signature:
00383    if (!png_sig_cmp(buf, 0, PNG_BYTES_TO_CHECK)) {
00384       // OK: it is a PNG file
00385       return fp;
00386    } else {
00387       // it is not a PNG file
00388       // close the file, return NULL:
00389       fclose(fp);
00390       return 0;
00391    }
00392 }
00393 
00394 /***********************************************************************
00395  * Method : Image::read_png
00396  * Params : char *file_name
00397  * Returns: int (success/failure)
00398  * Effects: opens file, reads data into memory (if it is a png file)
00399  ***********************************************************************/
00400 int
00401 Image::read_png(char *file)
00402 {
00403    FILE *fp = open_png(file);
00404    if (!fp)
00405       return 0;
00406 
00407    int success = read_png(fp);
00408 
00409    fclose(fp);
00410 
00411    if (success)
00412       return 1;
00413 
00414    err_msg("Image::read_png: error reading file: %s", file);
00415    return 0;
00416 }
00417 
00418 int
00419 Image::read_png(FILE* fp)
00420 {
00421    // reset and free up memory:
00422    clear();
00423 
00424    png_structp  png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0);
00425    if (!png_ptr) {
00426       err_msg("Image::read_png: png_create_read_struct() failed");
00427       return 0;
00428    }
00429 
00430    // Allocate/initialize the memory for image information
00431    png_infop info_ptr = png_create_info_struct(png_ptr);
00432    if (!info_ptr) {
00433       png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
00434       err_msg("Image::read_png: png_create_info_struct() failed");
00435       return 0;
00436    }
00437 
00438    // Set error handling
00439    if (setjmp(png_ptr->jmpbuf)) {
00440       // jump here from error encountered inside PNG code...
00441       // free all memory associated with the png_ptr and info_ptr
00442       png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
00443       return 0;
00444    }
00445 
00446    // Set up the input control (using standard C streams)
00447    //
00448    //   NB:
00449    //     aside from this file, all of jot uses iostreams.
00450    //     mixing the two I/O methods causes performance
00451    //     problems for low-level I/O buffering.
00452    //     we should look into setting up PNG to use
00453    //     iostreams.
00454    //
00455    png_init_io(png_ptr, fp);
00456 
00457    // indicate how much of the file has been read:
00458    png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK);
00459 
00460    // read information from the file before the first image data chunk
00461    png_read_info(png_ptr, info_ptr);
00462 
00463    // extract _width, _height, and other info from header:
00464    int bit_depth, color_type, interlace_type;
00465    png_get_IHDR(png_ptr, info_ptr,
00466                 (png_uint_32*)&_width,
00467                 (png_uint_32*)&_height,
00468                 &bit_depth,
00469                 &color_type,
00470                 &interlace_type,
00471                 NULL, NULL);
00472 
00473    // tell libpng to strip 16 bit/color files down to 8 bits/channel
00474    png_set_strip_16(png_ptr);
00475 
00476    // Extract multiple pixels with bit depths of 1, 2, and 4 from a single
00477    // byte into separate bytes (useful for paletted and grayscale images).
00478    png_set_packing(png_ptr);
00479 
00480    // Expand paletted colors into true RGB triplets
00481    if (color_type == PNG_COLOR_TYPE_PALETTE)
00482       png_set_expand(png_ptr);
00483 
00484    // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel
00485    if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
00486       png_set_expand(png_ptr);
00487 
00488    // Expand paletted or RGB images with transparency to full alpha channels
00489    // so the data will be available as RGBA quartets.
00490    if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
00491       png_set_expand(png_ptr);
00492 
00493    // update the header to reflect transformations applied:
00494    png_read_update_info(png_ptr, info_ptr);
00495 
00496    // now it's safe to read the size of a row:
00497    unsigned long row_bytes = png_get_rowbytes(png_ptr, info_ptr);
00498    _bpp = row_bytes / _width;
00499 
00500    // make sure bytes per pixel is a valid number:
00501    if (_bpp < 1 || _bpp > 4) {
00502       err_msg("Image::read_png: %d bytes/pixel not supported", _bpp);
00503    } else if (interlace_type != PNG_INTERLACE_NONE) {
00504       err_msg("Image::read_png: unsupported interlace type (%d)",
00505               interlace_type);
00506    } else if ((_data = new uchar [ size() ]) == 0) {
00507       err_ret("Image::read_png: can't allocate data");
00508    } else {
00509       _no_delete = 0;
00510 
00511       // no more excuses: read the image (inverted vertically):
00512       for (int y=_height-1; y>=0; y--)
00513          png_read_row(png_ptr, _data + y*row_bytes, 0);
00514 
00515       // read rest of file, and get additional
00516       // chunks in info_ptr - REQUIRED
00517       png_read_end(png_ptr, info_ptr);
00518    }
00519 
00520    // clean up after the read, and free any memory allocated - REQUIRED
00521    png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
00522 
00523    // return pixel data:
00524    if (_data)
00525       return 1;
00526    else {
00527       clear();
00528       return 0;
00529    }
00530 }
00531 
00532 /***********************************************************************
00533  * Method : Image::write_png
00534  * Params : char *file_name
00535  * Returns: int (success/failure)
00536  * Effects: opens file, writes data to disk
00537  ***********************************************************************/
00538 int
00539 Image::write_png(char *file) const
00540 {
00541    
00542    if (_width == 0 || _height == 0 || _data == 0) {
00543       err_msg("Image::write_png: image has no data");
00544       return 0;
00545    } else if (_bpp < 1 || _bpp > 4) {
00546       err_msg("Image::write_png: unsupported number of bytes/pixel (%d)",
00547               _bpp);
00548       return 0;
00549    }
00550 
00551    FILE* fp;
00552    if ((fp = fopen(file, "wb")) == 0) {
00553       err_ret("Image::write_png: can't open file %s", file);
00554       return 0;
00555    }
00556 
00557    // Create and initialize the png_struct with the desired error handler
00558    // functions.  If you want to use the default stderr and longjump method,
00559    // you can supply NULL for the last three parameters.  We also check that
00560    // the library version is compatible with the one used at compile time,
00561    // in case we are using dynamically linked libraries.  REQUIRED.
00562    png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,0,0,0);
00563    if (!png_ptr) {
00564       fclose(fp);
00565       err_msg("Image::write_png: png_create_write_struct() failed");
00566       return 0;
00567    }
00568 
00569    // Allocate/initialize the image information data.  REQUIRED
00570    png_infop info_ptr = png_create_info_struct(png_ptr);
00571    if (!info_ptr) {
00572       fclose(fp);
00573       png_destroy_write_struct(&png_ptr,  (png_infopp)NULL);
00574       err_msg("Image::write_png: png_create_info_struct() failed");
00575       return 0;
00576    }
00577 
00578    // Set error handling
00579    if (setjmp(png_ptr->jmpbuf)) {
00580       // jump here from error encountered inside PNG code...
00581       // free all memory associated with the png_ptr and info_ptr
00582       fclose(fp);
00583       png_destroy_write_struct(&png_ptr,  (png_infopp)NULL);
00584       err_msg("Image::write_png: error writing file %s", file);
00585       return 0;
00586    }
00587 
00588    // Set up the input control (using standard C streams)
00589    //
00590    //   see note re: C streams in read_png() above
00591    //
00592    png_init_io(png_ptr, fp);
00593 
00594    // set the image information:
00595    png_set_IHDR(png_ptr,
00596                 info_ptr,
00597                 _width,
00598                 _height,
00599                 8,                              // bit depth
00600                 ((_bpp==4) ? PNG_COLOR_TYPE_RGB_ALPHA :
00601                  (_bpp==3) ? PNG_COLOR_TYPE_RGB :
00602                  (_bpp==2) ? PNG_COLOR_TYPE_GRAY_ALPHA :
00603                  PNG_COLOR_TYPE_GRAY),
00604                 PNG_INTERLACE_NONE,
00605                 PNG_COMPRESSION_TYPE_BASE,
00606                 PNG_FILTER_TYPE_BASE);
00607 
00608    // set gamma
00609    double gamma = Config::get_var_dbl("JOT_GAMMA",0.45,true);
00610    png_set_gAMA(png_ptr, info_ptr, gamma);
00611                 
00612    // write the file header information.  REQUIRED
00613    png_write_info(png_ptr, info_ptr);
00614 
00615    // write the image data (inverted vertically):
00616    for (int y=_height-1; y>=0; y--)
00617       png_write_row(png_ptr, row(y));
00618 
00619    // It is REQUIRED to call this to finish writing
00620    png_write_end(png_ptr, info_ptr);
00621 
00622    // clean up after the write, and free any memory allocated
00623    png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
00624 
00625    // close the file
00626    fclose(fp);
00627 
00628    return 1;
00629 }
00630 
00631 uint 
00632 Image::pixel_rgba(uint x, uint y) const
00633 {
00634    if (x >= _width || y >= _height)
00635          return 0;
00636     uchar* val = row(y);
00637    uint ret=0;
00638    switch(_bpp) {
00639     case 4:
00640       // R,G,B,A unsigned char's
00641       ret = build_rgba(val[x*_bpp], val[x*_bpp+1], val[x*_bpp+2], val[x*_bpp+3]);
00642       break;
00643     case 3:
00644       // R,G,B unsigned char's
00645       ret = build_rgba(val[x*_bpp], val[x*_bpp+1], val[x*_bpp+2]);
00646       break;
00647     case 2:
00648       // luminance/alpha unsigned char's
00649       ret = build_rgba(val[x*_bpp], val[x*_bpp], val[x*_bpp], val[x*_bpp+1]);
00650       break;
00651     case 1:
00652       // luminance unsigned char's
00653       ret = build_rgba(val[x*_bpp], val[x*_bpp], val[x*_bpp]);
00654       break;
00655     default:
00656       // it's not in there
00657       err_msg( "Image::pixel_rgba: unknown bits per pixel (%d) in image", _bpp);
00658       return 0;
00659    }  
00660    return ret;
00661 }
00662    
00663 uint 
00664 Image::pixel_r(uint x, uint y) const
00665 {
00666    return rgba_to_r(pixel_rgba(x,y));
00667 }
00668 
00669 uint 
00670 Image::pixel_g(uint x, uint y) const
00671 {
00672    return rgba_to_g(pixel_rgba(x,y));  
00673 }
00674 
00675 uint 
00676 Image::pixel_b(uint x, uint y) const
00677 {
00678    return rgba_to_b(pixel_rgba(x,y));
00679 }  
00680 
00681 uint 
00682 Image::pixel_a(uint x, uint y) const
00683 {
00684    return rgba_to_a(pixel_rgba(x,y));  
00685 }
00686 
00687 /* end of file image.C */

Generated on Mon Sep 18 11:39:31 2006 for jot by  doxygen 1.4.4