Merge branch '138-toggle-free-look-with-hotkey' into 'main/atys-live'
[ryzomcore.git] / nel / src / misc / bitmap_jpeg.cpp
blob5709dda765948fc1060b7f7c73d3463b061eb063
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2014 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "stdmisc.h"
22 #include "nel/misc/bitmap.h"
24 #ifdef USE_JPEG
25 #define XMD_H
26 #undef FAR
27 #include "nel/misc/stream.h"
28 #include "nel/misc/file.h"
29 #include <csetjmp>
30 extern "C"
32 #ifdef NL_COMP_MINGW
33 # define HAVE_BOOLEAN
34 #endif
35 #include <jpeglib.h>
37 #endif
39 using namespace std;
41 #ifdef DEBUG_NEW
42 #define new DEBUG_NEW
43 #endif
45 namespace NLMISC
48 #ifdef USE_JPEG
50 static NLMISC::IStream *JPGStream = NULL;
51 static const uint32 JPGBufferSize = 4096;
52 static uint32 JPGStreamSize = 0;
53 static char JPGBuffer[JPGBufferSize];
55 struct my_error_mgr
57 struct jpeg_error_mgr pub;
58 jmp_buf setjmp_buffer;
61 void my_error_exit(j_common_ptr cinfo)
63 my_error_mgr *myerr = (my_error_mgr *) cinfo->err;
65 nlwarning("error while processing JPEG image");
67 longjmp(myerr->setjmp_buffer, 1);
70 static void jpgDecompressInit(j_decompress_ptr cinfo)
72 // get stream size if possible
73 if (JPGStream->seek(0, IStream::end))
74 JPGStreamSize = JPGStream->getPos();
75 else
76 nlwarning("can't get JPEG stream size");
78 // reset current position to the beginning
79 JPGStream->seek(0, IStream::begin);
81 cinfo->src->next_input_byte = (unsigned char *)JPGBuffer;
82 cinfo->src->bytes_in_buffer = 0;
85 static boolean jpgDecompressFill(j_decompress_ptr cinfo)
87 uint length = std::min(JPGBufferSize, JPGStreamSize - JPGStream->getPos());
89 try
91 JPGStream->serialBuffer((uint8*) JPGBuffer, length);
93 catch(...)
95 nlwarning("error while reading JPEG image");
96 cinfo->src->next_input_byte = (unsigned char *)JPGBuffer;
97 cinfo->src->bytes_in_buffer = 0;
98 return FALSE;
101 cinfo->src->next_input_byte = (unsigned char *)JPGBuffer;
102 cinfo->src->bytes_in_buffer = length;
103 return TRUE;
106 static void jpgDecompressSkip(j_decompress_ptr cinfo, long num_bytes)
108 if (num_bytes > 0)
110 while (num_bytes > (long) cinfo->src->bytes_in_buffer)
112 num_bytes -= (long) cinfo->src->bytes_in_buffer;
113 jpgDecompressFill(cinfo);
116 cinfo->src->next_input_byte += (size_t) num_bytes;
117 cinfo->src->bytes_in_buffer -= (size_t) num_bytes;
121 static void jpgDecompressTerm(j_decompress_ptr /* cinfo */)
125 static jpeg_source_mgr jpgSourceManager = { NULL, 0,
126 jpgDecompressInit, jpgDecompressFill, jpgDecompressSkip, jpeg_resync_to_restart, jpgDecompressTerm };
128 /*-------------------------------------------------------------------*\
129 readJPG
130 \*-------------------------------------------------------------------*/
131 uint8 CBitmap::readJPG( NLMISC::IStream &f )
133 if(!f.isReading()) return false;
135 struct jpeg_decompress_struct cinfo;
137 // set up errors manager
138 struct my_error_mgr jerr;
139 cinfo.err = jpeg_std_error(&jerr.pub);
140 jerr.pub.error_exit = my_error_exit;
142 if (setjmp(jerr.setjmp_buffer))
144 jpeg_destroy_decompress(&cinfo);
145 nlwarning("failed to setjump");
146 return 0;
149 // set the stream to read from
150 JPGStream = &f;
152 // init decompress
153 jpeg_create_decompress(&cinfo);
154 cinfo.src = &jpgSourceManager;
156 // read header of image
157 if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK)
159 jpeg_destroy_decompress(&cinfo);
160 nlwarning("failed to read header");
161 return 0;
164 uint dstChannels, srcChannels;
166 if (cinfo.jpeg_color_space == JCS_GRAYSCALE)
168 dstChannels = 1;
169 srcChannels = 1;
170 resize (cinfo.image_width, cinfo.image_height, _LoadGrayscaleAsAlpha ? Alpha : Luminance);
172 else
174 // force conversion of color spaces in RGB
175 dstChannels = 4;
176 srcChannels = 3;
177 cinfo.out_color_space = JCS_RGB;
178 resize (cinfo.image_width, cinfo.image_height, RGBA);
181 // start decompression of image data
182 if (!jpeg_start_decompress(&cinfo))
184 jpeg_destroy_decompress(&cinfo);
185 nlwarning("failed to start decompressing");
186 return 0;
189 JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo,
190 JPOOL_IMAGE, cinfo.output_width * cinfo.output_components, 1);
192 uint i, j;
194 while (cinfo.output_scanline < cinfo.output_height)
196 const uint offset = cinfo.output_scanline * _Width * dstChannels;
198 if (jpeg_read_scanlines(&cinfo, buffer, 1) != 1)
200 nlwarning("failed to read scanline");
201 break;
204 for (i = 0; i < _Width; i++)
206 for (j = 0; j < srcChannels; ++j)
207 _Data[0][offset+i*dstChannels+j] = buffer[0][i*srcChannels+j];
209 if (PixelFormat == RGBA)
210 _Data[0][offset+i*dstChannels+j] = 255;
214 if (!jpeg_finish_decompress(&cinfo))
215 nlwarning("failed to finish decompressing");
217 jpeg_destroy_decompress(&cinfo);
219 JPGStream = NULL;
221 return uint8(srcChannels * 8);
224 static void jpgCompressInit(j_compress_ptr cinfo)
226 cinfo->dest->next_output_byte = (unsigned char *)JPGBuffer;
227 cinfo->dest->free_in_buffer = JPGBufferSize;
230 static boolean jpgCompressEmpty(j_compress_ptr cinfo)
232 JPGStream->serialBuffer((uint8*) JPGBuffer, JPGBufferSize);
233 cinfo->dest->next_output_byte = (unsigned char *)JPGBuffer;
234 cinfo->dest->free_in_buffer = JPGBufferSize;
235 return TRUE;
238 static void jpgCompressTerm(j_compress_ptr cinfo)
240 if(JPGBufferSize - cinfo->dest->free_in_buffer > 0)
241 JPGStream->serialBuffer((uint8*) JPGBuffer, (uint)(JPGBufferSize - cinfo->dest->free_in_buffer));
244 static jpeg_destination_mgr jpgDestinationManager = { 0, 0,
245 jpgCompressInit, jpgCompressEmpty, jpgCompressTerm };
247 /*-------------------------------------------------------------------*\
248 writeJPG
249 \*-------------------------------------------------------------------*/
250 bool CBitmap::writeJPG( NLMISC::IStream &f, uint8 quality)
252 if (f.isReading()) return false;
254 if (PixelFormat > AlphaLuminance) return false;
255 if (!_Width || !_Height) return false;
257 struct jpeg_compress_struct cinfo;
259 // set up errors manager
260 struct my_error_mgr jerr;
261 cinfo.err = jpeg_std_error(&jerr.pub);
262 jerr.pub.error_exit = my_error_exit;
264 if (setjmp(jerr.setjmp_buffer))
266 jpeg_destroy_compress(&cinfo);
267 nlwarning("failed to setjump");
268 return false;
271 // set the stream to write to
272 JPGStream = &f;
274 // init compress
275 jpeg_create_compress(&cinfo);
277 uint srcChannels, dstChannels;
279 if (PixelFormat == RGBA)
281 srcChannels = 4;
282 dstChannels = cinfo.input_components = 3;
283 cinfo.in_color_space = JCS_RGB;
285 else
287 srcChannels = PixelFormat == AlphaLuminance ? 2:1;
288 dstChannels = cinfo.input_components = 1;
289 cinfo.in_color_space = JCS_GRAYSCALE;
292 cinfo.image_width = _Width;
293 cinfo.image_height = _Height;
294 cinfo.dest = &jpgDestinationManager;
296 // set default compression parameters
297 jpeg_set_defaults(&cinfo);
299 // set image quality
300 jpeg_set_quality(&cinfo, quality, TRUE);
302 // start to compress image
303 jpeg_start_compress(&cinfo, TRUE);
305 JSAMPROW row_pointer[1];
306 row_pointer[0] = new uint8[_Width*dstChannels];
308 uint i, j;
310 while (cinfo.next_scanline < cinfo.image_height)
312 const uint offset = cinfo.next_scanline * _Width * srcChannels;
314 for (i = 0; i < _Width; ++i)
316 for (j = 0; j < dstChannels; ++j)
318 row_pointer[0][i*dstChannels+j] = (uint8) _Data[0][offset + i*srcChannels+j];
322 // write image scanline
323 if (jpeg_write_scanlines(&cinfo, row_pointer, 1) != 1)
325 nlwarning("failed to write scanline");
326 break;
330 jpeg_finish_compress(&cinfo);
331 jpeg_destroy_compress(&cinfo);
333 delete row_pointer[0];
334 row_pointer[0] = NULL;
335 JPGStream = NULL;
337 return true;
340 #else
342 bool CBitmap::writeJPG( NLMISC::IStream &/* f */, uint8 /* quality */)
344 nlwarning ("You must compile NLMISC with USE_JPEG if you want jpeg support");
345 return false;
348 uint8 CBitmap::readJPG( NLMISC::IStream &/* f */)
350 nlwarning ("You must compile NLMISC with USE_JPEG if you want jpeg support");
351 return 0;
354 #endif