Main Page   Namespace List   Class Hierarchy   Compound List   File List   Compound Members   File Members  

provider_png.cpp

Go to the documentation of this file.
00001 /*
00002 
00003         ------------------------------------------------------------------------
00004         ClanLib, the platform independent game SDK.
00005 
00006         This library is distributed under the GNU LIBRARY GENERAL PUBLIC LICENSE
00007         version 2. See COPYING for details.
00008 
00009         For a total list of contributers see CREDITS.
00010 
00011         ------------------------------------------------------------------------
00012 
00013 
00014 
00015 */
00016 
00017 /* Known Bugs:
00018   ~~~~~~~~~~~~ 
00019    - this provider might not work with 16bit PNG's 
00020    - this provider might not work with some grayscale PNG's (don't remember which one)
00021 */
00022 
00023 #include "Core/precomp.h"
00024 #include "API/Display/Display/palette.h"
00025 #include "API/Display/Display/res_surface.h"
00026 #include "API/Core/Resources/resourceoptions.h"
00027 #include "API/Core/System/error.h" 
00028 #include "API/PNG/provider_png.h"
00029 
00030 // Workaround for a VC bug
00031 #ifdef WIN32
00032 #  define for if(0);else for
00033 #endif /* WIN32 */
00034 
00035 CL_Surface *CL_PNGProvider::create(CL_String file, CL_InputSourceProvider *provider, 
00036                                    bool transparent,
00037                                    bool ignore_alphachannel)
00038 {
00039         return CL_Surface::create(new CL_PNGProvider(file, provider, 
00040                                                      transparent, ignore_alphachannel), true);
00041 }
00042 
00043 CL_PNGProvider::CL_PNGProvider(CL_String name, CL_InputSourceProvider *_provider,
00044                                bool _transparent,
00045                                bool _ignore_alphachannel)
00046 {
00047         if (_provider == NULL)
00048         {
00049                 provider = CL_InputSourceProvider::create_file_provider(".");
00050         }
00051         else
00052         {
00053                 provider = _provider->clone();
00054         }
00055 
00056         ignore_alphachannel = _ignore_alphachannel;
00057         transparent = _transparent;
00058 
00059         indexed = true;
00060         palette = 0;
00061 
00062         trans_redcol = 0;
00063         trans_greencol = 0;
00064         trans_bluecol = 0;
00065 
00066         trans_col = -1;
00067         m_uses_src_colorkey = false;
00068 
00069         locked = 0;
00070         filename = name;
00071         image = NULL;
00072 }
00073 
00074 CL_PNGProvider::~CL_PNGProvider()
00075 {
00076         perform_unlock();
00077         delete provider;
00078 }
00079 
00080 unsigned int CL_PNGProvider::get_red_mask() const
00081 {
00082         if (pixel_format == PAL8)
00083                 return CL_Color::get_red_mask(RGBA8888); // Color mask don't make sense with indexed images
00084         else
00085                 return CL_Color::get_red_mask(pixel_format);
00086 }
00087 
00088 unsigned int CL_PNGProvider::get_green_mask() const
00089 {
00090         if (pixel_format == PAL8)
00091                 return CL_Color::get_green_mask(RGBA8888); // Color mask don't make sense with indexed images
00092         else
00093                 return CL_Color::get_green_mask(pixel_format);
00094 }
00095 
00096 unsigned int CL_PNGProvider::get_blue_mask() const
00097 {
00098         if (pixel_format == PAL8)
00099                 return CL_Color::get_blue_mask(RGBA8888); // Color mask don't make sense with indexed images
00100         else
00101                 return CL_Color::get_blue_mask(pixel_format);
00102 }
00103 
00104 unsigned int CL_PNGProvider::get_alpha_mask() const
00105 {
00106         if (pixel_format == PAL8)
00107                 return CL_Color::get_alpha_mask(RGBA8888); // Color mask don't make sense with indexed images
00108         else
00109                 return CL_Color::get_alpha_mask(pixel_format);
00110 }
00111 
00112 unsigned int CL_PNGProvider::get_depth() const
00113 {
00114         switch (pixel_format)
00115         {
00116         case RGBA8888:
00117                 return 32;
00118         case RGB888:
00119                 return 24;
00120         case PAL8:
00121                 return 8;
00122         default:
00123                 std::cerr << "CL_PNGProvider::get_depth: Unhandled pixel format: " << pixel_format << std::endl;
00124                 return 32;
00125         }
00126 }
00127   
00128 void CL_PNGProvider::read_data()
00129 {
00130         // initial fileinfo
00131         png_read_info(png_ptr, info_ptr); 
00132         
00133         // reduce 16bit/channel to 8Bit/channel
00134         png_set_strip_16(png_ptr);  
00135         
00136         // reread infostruct to reflect the made settings
00137         png_read_update_info(png_ptr, info_ptr); 
00138 
00139         width  = png_get_image_width(png_ptr,info_ptr);
00140         height = png_get_image_height(png_ptr,info_ptr);
00141         color_type = png_get_color_type(png_ptr,info_ptr);
00142 
00143         switch (color_type)
00144         {
00145         case PNG_COLOR_TYPE_GRAY:
00146                 read_data_grayscale ();
00147                 break;
00148         case PNG_COLOR_TYPE_GRAY_ALPHA:
00149                 read_data_grayscale_alpha ();
00150                 break;
00151         case PNG_COLOR_TYPE_PALETTE:
00152                 read_data_palette ();
00153                 break;
00154         case PNG_COLOR_TYPE_RGB:
00155                 read_data_rgb ();
00156                 break;
00157 
00158         case PNG_COLOR_TYPE_RGB_ALPHA:
00159                 read_data_rgba ();
00160                 break;
00161         default:
00162                 throw CL_Error ("CL_PNGProvider: Unsupported PNG format!");
00163                 break;
00164         }
00165 }
00166 
00167 void CL_PNGProvider::read_data_rgb()
00168 {
00169         indexed = false;
00170         pixel_format = RGB888;
00171 
00172         pitch = png_get_rowbytes(png_ptr, info_ptr);
00173 
00174         // This is deleted in the unlock () call
00175         image = new unsigned char[pitch * height];
00176 
00177         // setup pointers to each row in our target image
00178         png_bytep* row_pointers = new png_bytep[height];
00179         for (int y = 0; y < height; y++)
00180                 row_pointers[y] = image + (pitch * y);
00181         png_read_image(png_ptr, row_pointers);
00182         delete[] row_pointers;
00183 }
00184 
00185 void CL_PNGProvider::read_data_rgba()
00186 {
00187         indexed = false;
00188         pixel_format = RGBA8888;
00189 
00190         pitch = png_get_rowbytes(png_ptr, info_ptr);
00191 
00192         // This is deleted in the unlock () call
00193         image = new unsigned char[pitch * height];
00194 
00195         unsigned char* tmp_image = new unsigned char[pitch * height];
00196         // setup pointers to each row in our target image
00197         png_bytep* row_pointers = new png_bytep[height];
00198         for (int y = 0; y < height; y++)
00199                 row_pointers[y] = tmp_image + (pitch * y);
00200         png_read_image(png_ptr, row_pointers);
00201         delete[] row_pointers;
00202 
00203         if (!ignore_alphachannel)
00204         {
00205                 for (int i = 0; i < pitch * height; i += 4)
00206                 {
00207                         image[i + 0] = tmp_image[i + 3];
00208                         image[i + 1] = tmp_image[i + 2];
00209                         image[i + 2] = tmp_image[i + 1];
00210                         image[i + 3] = tmp_image[i + 0];
00211                 }
00212         }
00213         else
00214         {
00215                 for (int i = 0; i < pitch * height; i += 4)
00216                 {
00217                         image[i + 0] = 255;
00218                         image[i + 1] = tmp_image[i + 2];
00219                         image[i + 2] = tmp_image[i + 1];
00220                         image[i + 3] = tmp_image[i + 0];
00221                 }
00222         }
00223 
00224         delete[] tmp_image;
00225 }
00226 
00227 void CL_PNGProvider::read_data_grayscale()
00228 {
00229         pixel_format = RGB888;
00230         indexed = false;
00231 
00232         int bit_depth = png_get_bit_depth(png_ptr,info_ptr);
00233         int rowbytes  = png_get_rowbytes(png_ptr, info_ptr);
00234 
00235         pitch = rowbytes * 3;
00236 
00237         // We expand the grayscale values if necessare, so we always
00238         // get 8bits per pixel
00239         if (bit_depth < 8) png_set_expand (png_ptr);
00240 
00241         // Allocating the temporary buffer and fill it
00242         unsigned char* tmp_image = new unsigned char[height * rowbytes];
00243         png_bytep* row_pointers  = new png_bytep[height];
00244         for (int y = 0; y < height; y++)
00245                 row_pointers[y] = tmp_image + (rowbytes * y);
00246         png_read_image(png_ptr, row_pointers);
00247         delete[] row_pointers;
00248 
00249         image = new unsigned char[height * pitch];
00250         // Coverting the data in the tmp buffer to our final data
00251         for (int i = 0; i < rowbytes * height; i++)
00252         {
00253                 image[3*i + 0] = tmp_image[i];
00254                 image[3*i + 1] = tmp_image[i];
00255                 image[3*i + 2] = tmp_image[i];
00256         }
00257         delete[] tmp_image;
00258 }
00259 
00260 void CL_PNGProvider::read_data_grayscale_alpha()
00261 {
00262         pixel_format = RGBA8888;
00263         pitch = width * 4;
00264         indexed = false;
00265 
00266         int bit_depth = png_get_bit_depth(png_ptr,info_ptr);
00267         int rowbytes  = png_get_rowbytes(png_ptr, info_ptr);
00268 
00269         // We expand the grayscale values if necessare, so we always
00270         // get 8bits per pixel
00271         if (bit_depth < 8) png_set_expand (png_ptr);
00272 
00273         // Allocating the temporary buffer
00274         unsigned char* tmp_image = new unsigned char[height * rowbytes];
00275         png_bytep* row_pointers = new png_bytep[height];
00276         for (int y = 0; y < height; y++)
00277                 row_pointers[y] = tmp_image + (rowbytes * y);
00278         png_read_image(png_ptr, row_pointers);
00279         delete[] row_pointers;
00280 
00281         // Creating the final image out of tmp_image
00282         image = new unsigned char[width * pitch];
00283         if (!ignore_alphachannel)
00284         {
00285                 for (int i = 0; i < rowbytes * height; i += 2)
00286                 {
00287                         image[2*i + 0] = tmp_image[i + 1];
00288                         image[2*i + 1] = tmp_image[i + 0];
00289                         image[2*i + 2] = tmp_image[i + 0];
00290                         image[2*i + 3] = tmp_image[i + 0];
00291                 }
00292         } 
00293         else
00294         {
00295                 for (int i = 0; i < rowbytes * height; i += 2)
00296                 {
00297                         image[2*i + 0] = 255;
00298                         image[2*i + 1] = tmp_image[i + 0];
00299                         image[2*i + 2] = tmp_image[i + 0];
00300                         image[2*i + 3] = tmp_image[i + 0];
00301                 }
00302         }
00303 
00304         delete[] tmp_image;
00305 }
00306 
00307 void CL_PNGProvider::read_data_palette()
00308 {
00309         pixel_format = PAL8;
00310         indexed = true;
00311         palette = new CL_Palette ();
00312 
00313         int bit_depth = png_get_bit_depth(png_ptr,info_ptr);
00314         int rowbytes = png_get_rowbytes(png_ptr, info_ptr);
00315 
00316         unsigned char* tmp_image = new unsigned char[height * rowbytes];
00317         // Allocating the temporary buffer (will be deleted some
00318         // screens below
00319         png_bytep* row_pointers = new png_bytep[height];
00320         for (int y = 0; y < height; y++)
00321                 row_pointers[y] = tmp_image + (rowbytes * y);
00322         png_read_image(png_ptr, row_pointers);
00323         
00324         if (bit_depth == 8)
00325         {
00326                 // We don't need to convert the data, so we can use
00327                 // what we got
00328                 image = tmp_image;
00329                 pitch = rowbytes;
00330         }
00331         else
00332         {
00333                 // We need to convert the data
00334                 pitch = width;
00335 
00336                 switch (bit_depth)
00337                 {
00338                 case 1:
00339                         image = new unsigned char[height * rowbytes * 8];
00340                         for (int y = 0; y < height; y++)
00341                         {
00342                                 for (int x = 0; x < rowbytes; x++) 
00343                                 {
00344                                         image[y*pitch + 8*x + 0] = row_pointers[y][x] >> 7;
00345                                         image[y*pitch + 8*x + 1] = row_pointers[y][x] >> 6 & 0x1;
00346                                         image[y*pitch + 8*x + 2] = row_pointers[y][x] >> 5 & 0x1;
00347                                         image[y*pitch + 8*x + 3] = row_pointers[y][x] >> 4 & 0x1;
00348                                         image[y*pitch + 8*x + 4] = row_pointers[y][x] >> 3 & 0x1;
00349                                         image[y*pitch + 8*x + 5] = row_pointers[y][x] >> 2 & 0x1;
00350                                         image[y*pitch + 8*x + 6] = row_pointers[y][x] >> 1 & 0x1;
00351                                         image[y*pitch + 8*x + 7] = row_pointers[y][x] & 0x1;
00352                                 }
00353                         }
00354                         break;
00355                 case 2:
00356                         image = new unsigned char[height * rowbytes * 4];
00357                         for (int y = 0; y < height; y++)
00358                         {
00359                                 for (int x = 0; x < rowbytes; x++) 
00360                                 {
00361                                                 image[y*pitch + 4*x + 0] = row_pointers[y][x] >> 6;
00362                                                 image[y*pitch + 4*x + 1] = row_pointers[y][x] >> 4 & 0x3;
00363                                                 image[y*pitch + 4*x + 2] = row_pointers[y][x] >> 2 & 0x3;
00364                                                 image[y*pitch + 4*x + 3] = row_pointers[y][x] & 0x3;
00365                                 }
00366                         }
00367                         break;
00368                 case 4:
00369                         image = new unsigned char[height * rowbytes * 2];
00370                         for (int y = 0; y < height; y++)
00371                         {
00372                                 for (int x = 0; x < rowbytes; x++)
00373                                 {
00374                                                 image[y*pitch + 2*x + 0] = row_pointers[y][x] >> 4;
00375                                                 image[y*pitch + 2*x + 1] = row_pointers[y][x] & 0x0f;
00376                                 }
00377                         }
00378                         break;
00379                 default:
00380                         throw CL_Error ("CL_PNGProvider: Unhandled bit depth");
00381                 }
00382                 delete[] tmp_image;
00383         }
00384         delete[] row_pointers;
00385 
00386         // Read the png palette and create the CL_Palette 
00387         png_colorp png_palette;
00388         png_get_PLTE(png_ptr, info_ptr, &png_palette, &palette->num_colors);
00389         palette->palette = new unsigned char [palette->num_colors * 3];
00390         for (int k = 0; k < palette->num_colors; k++)
00391         {
00392                 palette->palette[k*3 + 0] = png_palette[k].red;
00393                 palette->palette[k*3 + 1] = png_palette[k].green;
00394                 palette->palette[k*3 + 2] = png_palette[k].blue;
00395         }
00396 
00397         // Reading and setting up the transparent colors from the image
00398         int num_trans = 0;
00399         png_color_16p trans_values;
00400         png_bytep trans;
00401         png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values);
00402         
00403         // Setting up the transparent color
00404         // FIXME: Can we only support a single transparent color?
00405         if (num_trans == 1)
00406         {
00407                 trans_col = trans[0];
00408                 m_uses_src_colorkey = true;
00409         }
00410         else if (num_trans > 1)
00411         {
00412                 std::cerr << "CL_PNGProvider: Only one transpranent color is supported" << std::endl;
00413                 trans_col = trans[0];
00414                 // We collaps all transparent colors to a single one
00415                 for (int j = 0; j < pitch * height; j++)
00416                         for (int i = 1; i < num_trans; i++)
00417                         {
00418                                 if (trans[i] == image[j])
00419                                         image[j] = trans_col;
00420                         }
00421                 m_uses_src_colorkey = true;                     
00422         }
00423 }
00424 
00425 void* CL_PNGProvider::get_data() const
00426 {
00427         return image;
00428 }
00429 
00430 /*
00431         Lock the surfaceprovider - which basically means open the file
00432         and read the image into a temporary memory buffer - until
00433         unlock() is called.
00434 */
00435 void CL_PNGProvider::perform_lock()
00436 {
00437         if (locked) return;
00438 
00439         //setting up PNGLIB stuff
00440         png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
00441         
00442         if (!png_ptr) 
00443           throw CL_Error ("CL_PNGProvider: png_create_read_struct() failed");
00444         
00445         info_ptr = png_create_info_struct(png_ptr);
00446 
00447         if (!info_ptr)
00448         {
00449                 png_destroy_read_struct(&png_ptr,
00450                                         (png_infopp)NULL, (png_infopp)NULL);
00451                 throw CL_Error ("CL_PNGProvider: png_create_info_struct() failed");
00452         }
00453 
00454         end_info = png_create_info_struct(png_ptr);
00455 
00456         if (!end_info)
00457         {
00458                 png_destroy_read_struct(&png_ptr, &info_ptr,
00459                                         (png_infopp)NULL);
00460                 cl_assert(false);
00461         }   
00462         if (setjmp(png_ptr->jmpbuf))
00463         {
00464                 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
00465                 cl_assert(false);
00466         }  
00467         
00468         cl_assert(provider != NULL);
00469         input_source = provider->open_source(filename);
00470         cl_assert(input_source!=NULL);
00471 
00472         // tell libpng form whom it get the fileData
00473         png_set_read_fn(png_ptr, this, &CL_PNGProvider::pngread_file);
00474 
00475         // reading the header infos and actually read data ...
00476         read_data();
00477 
00478         // remove our data_provider from libpng
00479         png_set_read_fn(png_ptr,NULL,NULL);
00480 
00481         // free memory ...
00482         png_destroy_read_struct(&png_ptr, &info_ptr,&end_info);
00483 
00484         delete input_source;
00485 
00486         locked++;
00487 }
00488 
00489 void CL_PNGProvider::perform_unlock()
00490 {
00491         locked--;
00492 
00493         delete[] image;
00494 
00495         if (palette) delete palette;
00496 
00497         image =  NULL;
00498 }
00499 
00500 // Resource support:
00501 class CL_PNG_ResourceSource : public CL_ResourceSource_Surface
00502 {
00503 public:
00504         virtual const char *get_name() { return "png"; }
00505 
00506         virtual bool can_create(std::string ext,
00507                 CL_ResourceOptions *options)
00508         {
00509                 if (ext == ".png") return true;
00510                 if (options->exists("png")) return true;
00511 
00512                 return false;
00513         }
00514 
00515         virtual CL_SurfaceProvider *create(std::string filename,
00516            CL_ResourceOptions *options,
00517            CL_ResourceManager *parent)
00518         {
00519                 return new CL_PNGProvider(filename.c_str(), NULL);
00520         }
00521 
00522 };
00523 
00524 static CL_PNG_ResourceSource *res_source_png = NULL;
00525 
00526 void CL_SetupPNG::init()
00527 {
00528         res_source_png = new CL_PNG_ResourceSource;
00529 }
00530 
00531 void CL_SetupPNG::deinit()
00532 {
00533         delete res_source_png;
00534         res_source_png = NULL;
00535 }

Generated at Wed Apr 4 19:54:02 2001 for ClanLib by doxygen1.2.6 written by Dimitri van Heesch, © 1997-2001