예제 #1
0
/*---------------------------------------------------------------------------
   Loads a PNG file.

   Image  : Image will be returned there.
   Path   : Path to image file, relative to the current working directory.
   Gamma  : Display gamma to apply on textures, 2.2 is typical.
  ---------------------------------------------------------------------------*/
void PNG::Load(Texture &Image, const std::string &Path, float Gamma)
   {
   Destroy();

   //Important - set class to reading mode
   Mode = PNG::ModeRead;

   #if defined (WINDOWS)
      if (fopen_s(&File, Path.c_str(), "rb") != 0)
         {throw dexception("Failed to open file: %s.", Path.c_str());}
   #else
      File = fopen(Path.c_str(), "rb");
      if (File == nullptr) {throw dexception("Failed to open file: %s.", Path.c_str());}
   #endif

   uint8 Header[PNG::HeaderSize];
   if (fread((png_bytep)Header, 1, PNG::HeaderSize, File) == 0)
      {throw dexception("fread( ) failed.");}

   if (!png_check_sig(Header, PNG::HeaderSize))
      {throw dexception("Not a valid PNG file.");}

   PngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
   if (PngPtr == nullptr) {throw dexception("png_create_read_struct( ) failed.");}

   InfoPtr = png_create_info_struct(PngPtr);
   if (InfoPtr == nullptr) {throw dexception("png_create_info_struct( ) failed.");}

   if (setjmp(png_jmpbuf(PngPtr)))
      {throw dexception("setjmp( ) failed.");}

   //Read into structures
   png_init_io(PngPtr, File);
   png_set_sig_bytes(PngPtr, PNG::HeaderSize);
   png_read_info(PngPtr, InfoPtr);

   //Determine image attributes
   vector2<png_uint_32> Res;
   int BitDepth, ColourType;
   png_get_IHDR(PngPtr, InfoPtr, &Res.U, &Res.V, &BitDepth, &ColourType, nullptr, nullptr, nullptr);

   Texture::TexType Type = Texture::TypeRGB;
   switch (ColourType)
      {
      case PNG_COLOR_TYPE_GRAY :
         if (BitDepth < 8)
            {
            Type = Texture::TypeLum;
            png_set_expand_gray_1_2_4_to_8(PngPtr);
            }
         else if (BitDepth == 16)
            {
            Type = Texture::TypeDepth;
            }
         break;

      case PNG_COLOR_TYPE_GRAY_ALPHA :
         Type = Texture::TypeRGBA;
         png_set_gray_to_rgb(PngPtr);
         break;

      case PNG_COLOR_TYPE_PALETTE :
         Type = Texture::TypeRGB;
         png_set_palette_to_rgb(PngPtr);
         break;

      case PNG_COLOR_TYPE_RGB :
         Type = Texture::TypeRGB;
         png_set_strip_alpha(PngPtr); //If libpng reports RGB, make sure we completely strip alpha
         break;

      case PNG_COLOR_TYPE_RGB_ALPHA :
         Type = Texture::TypeRGBA;
         break;

      default : throw dexception("Unknown colour type.");
      }

   //Convert palette alpha into a separate alpha channel
   if (png_get_valid(PngPtr, InfoPtr, PNG_INFO_tRNS))
      {
      png_set_tRNS_to_alpha(PngPtr);
      if (ColourType == PNG_COLOR_TYPE_PALETTE) {Type = Texture::TypeRGBA;}
      }

   //Force 8 bit per channel
   if (BitDepth >= 16)
      {
      if (Type != Texture::TypeDepth) {png_set_strip_16(PngPtr);}
      }
   else if (BitDepth < 8)
      {
      png_set_packing(PngPtr);
      }

   //Specify display and file gamma (assume 0.45455 for missing file gamma)
   double FileGamma;
   if (!png_get_gAMA(PngPtr, InfoPtr, &FileGamma)) {FileGamma = 0.45455;}
   png_set_gamma(PngPtr, Gamma, FileGamma);

   //Swap byte order for int16 values on big endian machines
   if ((BitDepth >= 16) && !Math::MachineLittleEndian())
      {png_set_swap(PngPtr);}

   //Apply the above transformation settings
   png_read_update_info(PngPtr, InfoPtr);

   //Allocate image
   Image.Create(cast_vector2(uint, Res), Type);

   if (Image.GetBytesPerLine() < (usize)png_get_rowbytes(PngPtr, InfoPtr))
      {throw dexception("Incorrect scan line size for allocated image.");}

   //Populate row pointer array
   Rows.Create((usize)Res.V);
   for (uiter I = 0; I < (usize)Res.V; I++)
      {
      Rows[I] = Image.Address(0, I);
      }

   //Decode
   png_read_image(PngPtr, Rows.Pointer());
   png_read_end(PngPtr, InfoPtr);

   //Clean up
   Destroy();
   }
예제 #2
0
/*---------------------------------------------------------------------------
   Saves a PNG file.

   Image    : Image to save.
   Path     : Path to image file, relative to the current working directory.
   Compress : Specifies the compression level to use for encoding.
  ---------------------------------------------------------------------------*/
void PNG::Save(const Texture &Image, const std::string &Path, CompLevel Compress)
   {
   Destroy();

   //Important - set class to writing mode
   Mode = PNG::ModeWrite;

   vector2u Res = Image.Resolution();
   if (Res.U < 1 || Res.V < 1) {return;}

   #if defined (WINDOWS)
      if (fopen_s(&File, Path.c_str(), "wb") != 0)
         {throw dexception("Failed to open file: %s.", Path.c_str());}
   #else
      File = fopen(Path.c_str(), "wb");
      if (File == nullptr) {throw dexception("Failed to open file: %s.", Path.c_str());}
   #endif

   PngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
   if (PngPtr == nullptr) {throw dexception("png_create_write_struct( ) failed.");}

   InfoPtr = png_create_info_struct(PngPtr);
   if (InfoPtr == nullptr) {throw dexception("png_create_info_struct( ) failed.");}

   if (setjmp(png_jmpbuf(PngPtr)))
      {throw dexception("setjmp( ) failed.");}

   png_init_io(PngPtr, File);

   png_set_compression_level(PngPtr, (int)Compress);

   int BitDepth, ColourType;
   switch (Image.DataType())
      {
      case Texture::TypeAlpha : BitDepth = 8;  ColourType = PNG_COLOR_TYPE_GRAY; break;
      case Texture::TypeLum   : BitDepth = 8;  ColourType = PNG_COLOR_TYPE_GRAY; break;
      case Texture::TypeDepth : BitDepth = 16; ColourType = PNG_COLOR_TYPE_GRAY; break;
      case Texture::TypeRGB   : BitDepth = 8;  ColourType = PNG_COLOR_TYPE_RGB; break;
      case Texture::TypeRGBA  : BitDepth = 8;  ColourType = PNG_COLOR_TYPE_RGB_ALPHA; break;
      default : throw dexception("Unsupported image type."); break;
      }

   png_set_IHDR(PngPtr, InfoPtr, (png_uint_32)Res.U, (png_uint_32)Res.V, BitDepth, ColourType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);

   png_set_sRGB(PngPtr, InfoPtr, PNG_sRGB_INTENT_ABSOLUTE);
   png_set_sRGB_gAMA_and_cHRM(PngPtr, InfoPtr, PNG_sRGB_INTENT_ABSOLUTE);

   png_color_16 BackGnd;
   BackGnd.red   = 0;
   BackGnd.green = 0;
   BackGnd.blue  = 0;
   png_set_bKGD(PngPtr, InfoPtr, &BackGnd);

   png_text PngText[2];
   memset(PngText, 0, sizeof(PngText));

   PngText[0].compression = PNG_TEXT_COMPRESSION_NONE;
   PngText[0].key = const_cast<png_charp>("Software");
   PngText[0].text = const_cast<png_charp>(AppName);
   PngText[0].text_length = strlen(PngText[0].text);

   PngText[1].compression = PNG_TEXT_COMPRESSION_NONE;
   PngText[1].key = const_cast<png_charp>("Author");
   PngText[1].text = const_cast<png_charp>(AppAuthor);
   PngText[1].text_length = strlen(PngText[1].text);

   png_set_text(PngPtr, InfoPtr, PngText, 2);

   png_write_info(PngPtr, InfoPtr);

   //Populate row pointer array
   Rows.Create((usize)Res.V);
   for (uiter I = 0; I < (usize)Res.V; I++)
      {
      Rows[I] = Image.Address(0, I);
      }

   //Encode
   png_write_image(PngPtr, Rows.Pointer());
   png_write_end(PngPtr, nullptr);

   //Clean up
   Destroy();
   }