ImageLoader.cpp
1 //
3 // SFML - Simple and Fast Multimedia Library
4 // Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
5 //
6 // This software is provided 'as-is', without any express or implied warranty.
7 // In no event will the authors be held liable for any damages arising from the use of this software.
8 //
9 // Permission is granted to anyone to use this software for any purpose,
10 // including commercial applications, and to alter it and redistribute it freely,
11 // subject to the following restrictions:
12 //
13 // 1. The origin of this software must not be misrepresented;
14 // you must not claim that you wrote the original software.
15 // If you use this software in a product, an acknowledgment
16 // in the product documentation would be appreciated but is not required.
17 //
18 // 2. Altered source versions must be plainly marked as such,
19 // and must not be misrepresented as being the original software.
20 //
21 // 3. This notice may not be removed or altered from any source distribution.
22 //
24 
26 // Headers
28 #include <SFML/Graphics/ImageLoader.hpp>
29 extern "C"
30 {
31  #include <jpeglib.h>
32  #include <jerror.h>
33 }
34 #include <png.h>
35 #include <SOIL/SOIL.h>
36 #include <iostream>
37 #include <string.h>
38 
39 
40 namespace
41 {
45  void PngErrorHandler(png_structp Png, png_const_charp Message)
46  {
47  std::cerr << "Failed to write PNG image. Reason : " << Message << std::endl;
48 #if PNG_LIBPNG_VER_MAJOR >= 1 && PNG_LIBPNG_VER_MINOR >= 4
49  longjmp(png_jmpbuf(Png), 1);
50 #else
51  longjmp(Png->jmpbuf, 1);
52 #endif
53  }
54 }
55 
56 
57 namespace sf
58 {
59 namespace priv
60 {
64 ImageLoader& ImageLoader::GetInstance()
65 {
66  static ImageLoader Instance;
67 
68  return Instance;
69 }
70 
71 
75 ImageLoader::ImageLoader()
76 {
77  // Nothing to do
78 }
79 
80 
84 ImageLoader::~ImageLoader()
85 {
86  // Nothing to do
87 }
88 
89 
93 bool ImageLoader::LoadImageFromFile(const std::string& Filename, std::vector<Color>& Pixels, unsigned int& Width, unsigned int& Height)
94 {
95  // Clear the array (just in case)
96  Pixels.clear();
97 
98  // Load the image and get a pointer to the pixels in memory
99  int ImgWidth, ImgHeight, ImgChannels;
100  unsigned char* PixelsPtr = SOIL_load_image(Filename.c_str(), &ImgWidth, &ImgHeight, &ImgChannels, SOIL_LOAD_RGBA);
101 
102  if (PixelsPtr)
103  {
104  // Assign the image properties
105  Width = ImgWidth;
106  Height = ImgHeight;
107 
108  // Copy the loaded pixels to the pixel buffer
109  Pixels.resize(Width * Height);
110  memcpy(&Pixels[0], PixelsPtr, Width * Height * 4);
111 
112  // Free the loaded pixels (they are now in our own pixel buffer)
113  SOIL_free_image_data(PixelsPtr);
114 
115  return true;
116  }
117  else
118  {
119  // Error, failed to load the image
120  std::cerr << "Failed to load image \"" << Filename << "\". Reason : " << SOIL_last_result() << std::endl;
121 
122  return false;
123  }
124 }
125 
126 
130 bool ImageLoader::LoadImageFromMemory(const char* Data, std::size_t SizeInBytes, std::vector<Color>& Pixels, unsigned int& Width, unsigned int& Height)
131 {
132  // Clear the array (just in case)
133  Pixels.clear();
134 
135  // Load the image and get a pointer to the pixels in memory
136  const unsigned char* Buffer = reinterpret_cast<const unsigned char*>(Data);
137  int Size = static_cast<int>(SizeInBytes);
138  int ImgWidth, ImgHeight, ImgChannels;
139  unsigned char* PixelsPtr = SOIL_load_image_from_memory(Buffer, Size, &ImgWidth, &ImgHeight, &ImgChannels, SOIL_LOAD_RGBA);
140 
141  if (PixelsPtr)
142  {
143  // Assign the image properties
144  Width = ImgWidth;
145  Height = ImgHeight;
146 
147  // Copy the loaded pixels to the pixel buffer
148  Pixels.resize(Width * Height);
149  memcpy(&Pixels[0], PixelsPtr, Width * Height * 4);
150 
151  // Free the loaded pixels (they are now in our own pixel buffer)
152  SOIL_free_image_data(PixelsPtr);
153 
154  return true;
155  }
156  else
157  {
158  // Error, failed to load the image
159  std::cerr << "Failed to load image from memory. Reason : " << SOIL_last_result() << std::endl;
160 
161  return false;
162  }
163 }
164 
165 
169 bool ImageLoader::SaveImageToFile(const std::string& Filename, const std::vector<Color>& Pixels, unsigned int Width, unsigned int Height)
170 {
171  // Deduce the image type from its extension
172  int Type = -1;
173  if (Filename.size() > 3)
174  {
175  std::string Extension = Filename.substr(Filename.size() - 3);
176  if (Extension == "bmp" || Extension == "BMP") Type = SOIL_SAVE_TYPE_BMP;
177  else if (Extension == "tga" || Extension == "TGA") Type = SOIL_SAVE_TYPE_TGA;
178  else if (Extension == "dds" || Extension == "DDS") Type = SOIL_SAVE_TYPE_DDS;
179 
180  // Special handling for PNG and JPG -- not handled by SOIL
181  else if (Extension == "png" || Extension == "PNG") return WritePng(Filename, Pixels, Width, Height);
182  else if (Extension == "jpg" || Extension == "JPG") return WriteJpg(Filename, Pixels, Width, Height);
183  }
184 
185  if (Type == -1)
186  {
187  // Error, incompatible type
188  std::cerr << "Failed to save image \"" << Filename << "\". Reason : this image format is not supported" << std::endl;
189  return false;
190  }
191 
192  // Finally save the image
193  const unsigned char* PixelsPtr = reinterpret_cast<const unsigned char*>(&Pixels[0]);
194  if (!SOIL_save_image(Filename.c_str(), Type, static_cast<int>(Width), static_cast<int>(Height), 4, PixelsPtr))
195  {
196  // Error, failed to save the image
197  std::cerr << "Failed to save image \"" << Filename << "\". Reason : " << SOIL_last_result() << std::endl;
198  return false;
199  }
200 
201  return true;
202 }
203 
204 
208 bool ImageLoader::WriteJpg(const std::string& Filename, const std::vector<Color>& Pixels, unsigned int Width, unsigned int Height)
209 {
210  // Open the file to write in
211  FILE* File = fopen(Filename.c_str(), "wb");
212  if (!File)
213  {
214  std::cerr << "Failed to save image file \"" << Filename << "\". Reason : cannot open file" << std::endl;
215  return false;
216  }
217 
218  // Initialize the error handler
219  jpeg_compress_struct CompressInfo;
220  jpeg_error_mgr ErrorManager;
221  CompressInfo.err = jpeg_std_error(&ErrorManager);
222 
223  // Initialize all the writing and compression infos
224  jpeg_create_compress(&CompressInfo);
225  CompressInfo.image_width = Width;
226  CompressInfo.image_height = Height;
227  CompressInfo.input_components = 3;
228  CompressInfo.in_color_space = JCS_RGB;
229  jpeg_stdio_dest(&CompressInfo, File);
230  jpeg_set_defaults(&CompressInfo);
231  jpeg_set_quality(&CompressInfo, 90, TRUE);
232 
233  // Get rid of the aplha channel
234  std::vector<Uint8> PixelsBuffer(Width * Height * 3);
235  for (std::size_t i = 0; i < Pixels.size(); ++i)
236  {
237  PixelsBuffer[i * 3 + 0] = Pixels[i].r;
238  PixelsBuffer[i * 3 + 1] = Pixels[i].g;
239  PixelsBuffer[i * 3 + 2] = Pixels[i].b;
240  }
241  Uint8* PixelsPtr = &PixelsBuffer[0];
242 
243  // Start compression
244  jpeg_start_compress(&CompressInfo, TRUE);
245 
246  // Write each row of the image
247  while (CompressInfo.next_scanline < CompressInfo.image_height)
248  {
249  JSAMPROW RowPointer = PixelsPtr + (CompressInfo.next_scanline * Width * 3);
250  jpeg_write_scanlines(&CompressInfo, &RowPointer, 1);
251  }
252 
253  // Finish compression
254  jpeg_finish_compress(&CompressInfo);
255  jpeg_destroy_compress(&CompressInfo);
256 
257  // Close the file
258  fclose(File);
259 
260  return true;
261 }
262 
263 
267 bool ImageLoader::WritePng(const std::string& Filename, const std::vector<Color>& Pixels, unsigned int Width, unsigned int Height)
268 {
269  // Open the file to write in
270  FILE* File = fopen(Filename.c_str(), "wb");
271  if (!File)
272  {
273  std::cerr << "Failed to save image file \"" << Filename << "\". Reason : cannot open file" << std::endl;
274  return false;
275  }
276 
277  // Create the main PNG structure
278  png_structp Png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, &PngErrorHandler, NULL);
279  if (!Png)
280  {
281  fclose(File);
282  std::cerr << "Failed to save image file \"" << Filename << "\". Reason : cannot allocate PNG write structure" << std::endl;
283  return false;
284  }
285 
286  // Initialize the image informations
287  png_infop PngInfo = png_create_info_struct(Png);
288  if (!PngInfo)
289  {
290  fclose(File);
291  png_destroy_write_struct(&Png, NULL);
292  std::cerr << "Failed to save image file \"" << Filename << "\". Reason : cannot allocate PNG info structure" << std::endl;
293  return false;
294  }
295 
296  // For proper error handling...
297 #if PNG_LIBPNG_VER_MAJOR >= 1 && PNG_LIBPNG_VER_MINOR >= 4
298  if (setjmp(png_jmpbuf(Png)))
299 #else
300  if (setjmp(Png->jmpbuf))
301 #endif
302  {
303  png_destroy_write_struct(&Png, &PngInfo);
304  return false;
305  }
306 
307  // Link the file to the PNG structure
308  png_init_io(Png, File);
309 
310  // Set the image informations
311  png_set_IHDR(Png, PngInfo, Width, Height, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
312 
313  // Write the header
314  png_write_info(Png, PngInfo);
315 
316  // Get the pointers to the pixels rows into an array
317  png_byte* PixelsPtr = (png_byte*)&Pixels[0];
318  std::vector<png_byte*> RowPointers(Height);
319  for (unsigned int i = 0; i < Height; ++i)
320  {
321  RowPointers[i] = PixelsPtr;
322  PixelsPtr += Width * 4;
323  }
324 
325  // Write pixels row by row
326  png_set_rows(Png, PngInfo, &RowPointers[0]);
327  png_write_png(Png, PngInfo, PNG_TRANSFORM_IDENTITY, NULL);
328 
329  // Finish writing the file
330  png_write_end(Png, PngInfo);
331 
332  // Cleanup resources
333  png_destroy_write_struct(&Png, &PngInfo);
334  fclose(File);
335 
336  return true;
337 }
338 
339 } // namespace priv
340 
341 } // namespace sf