1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2014 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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/>.
22 #include "nel/misc/bitmap.h"
27 #include "nel/misc/stream.h"
28 #include "nel/misc/file.h"
50 static NLMISC::IStream
*JPGStream
= NULL
;
51 static const uint32 JPGBufferSize
= 4096;
52 static uint32 JPGStreamSize
= 0;
53 static char JPGBuffer
[JPGBufferSize
];
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();
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());
91 JPGStream
->serialBuffer((uint8
*) JPGBuffer
, length
);
95 nlwarning("error while reading JPEG image");
96 cinfo
->src
->next_input_byte
= (unsigned char *)JPGBuffer
;
97 cinfo
->src
->bytes_in_buffer
= 0;
101 cinfo
->src
->next_input_byte
= (unsigned char *)JPGBuffer
;
102 cinfo
->src
->bytes_in_buffer
= length
;
106 static void jpgDecompressSkip(j_decompress_ptr cinfo
, long num_bytes
)
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 /*-------------------------------------------------------------------*\
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");
149 // set the stream to read from
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");
164 uint dstChannels
, srcChannels
;
166 if (cinfo
.jpeg_color_space
== JCS_GRAYSCALE
)
170 resize (cinfo
.image_width
, cinfo
.image_height
, _LoadGrayscaleAsAlpha
? Alpha
: Luminance
);
174 // force conversion of color spaces in RGB
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");
189 JSAMPARRAY buffer
= (*cinfo
.mem
->alloc_sarray
)((j_common_ptr
) &cinfo
,
190 JPOOL_IMAGE
, cinfo
.output_width
* cinfo
.output_components
, 1);
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");
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
);
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
;
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 /*-------------------------------------------------------------------*\
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");
271 // set the stream to write to
275 jpeg_create_compress(&cinfo
);
277 uint srcChannels
, dstChannels
;
279 if (PixelFormat
== RGBA
)
282 dstChannels
= cinfo
.input_components
= 3;
283 cinfo
.in_color_space
= JCS_RGB
;
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
);
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
];
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");
330 jpeg_finish_compress(&cinfo
);
331 jpeg_destroy_compress(&cinfo
);
333 delete row_pointer
[0];
334 row_pointer
[0] = NULL
;
342 bool CBitmap::writeJPG( NLMISC::IStream
&/* f */, uint8
/* quality */)
344 nlwarning ("You must compile NLMISC with USE_JPEG if you want jpeg support");
348 uint8
CBitmap::readJPG( NLMISC::IStream
&/* f */)
350 nlwarning ("You must compile NLMISC with USE_JPEG if you want jpeg support");