ImageLoader.cpp
1
2//
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>
29extern "C"
30{
31 #include <jpeglib.h>
32 #include <jerror.h>
33}
34#include <png.h>
35#include <SOIL/SOIL.h>
36#include <iostream>
37
38#include <string.h>
39
40namespace
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
57namespace sf
58{
59namespace priv
60{
64ImageLoader& ImageLoader::GetInstance()
65{
66 static ImageLoader Instance;
67
68 return Instance;
69}
70
71
75ImageLoader::ImageLoader()
76{
77 // Nothing to do
78}
79
80
84ImageLoader::~ImageLoader()
85{
86 // Nothing to do
87}
88
89
93bool 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
130bool 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
169bool 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
208bool 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
267bool 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