]> git.lizzy.rs Git - irrlicht.git/blob - source/Irrlicht/CImageWriterJPG.cpp
Merging r6039 through r6072 from trunk to ogl-es branch.
[irrlicht.git] / source / Irrlicht / CImageWriterJPG.cpp
1 // Copyright (C) 2002-2012 Nikolaus Gebhardt\r
2 // This file is part of the "Irrlicht Engine".\r
3 // For conditions of distribution and use, see copyright notice in irrlicht.h\r
4 \r
5 #include "CImageWriterJPG.h"\r
6 \r
7 #ifdef _IRR_COMPILE_WITH_JPG_WRITER_\r
8 \r
9 #include "CColorConverter.h"\r
10 #include "IWriteFile.h"\r
11 #include "CImage.h"\r
12 #include "irrString.h"\r
13 #include "os.h"\r
14 \r
15 #ifdef _IRR_COMPILE_WITH_LIBJPEG_\r
16 #include <stdio.h> // required for jpeglib.h\r
17 extern "C"\r
18 {\r
19 #ifndef _IRR_USE_NON_SYSTEM_JPEG_LIB_\r
20         #include <jpeglib.h>\r
21         #include <jerror.h>\r
22 #else\r
23         #include "jpeglib/jpeglib.h"\r
24         #include "jpeglib/jerror.h"\r
25 #endif\r
26 }\r
27 \r
28 \r
29 namespace irr\r
30 {\r
31 namespace video\r
32 {\r
33 \r
34 // The writer uses a 4k buffer and flushes to disk each time it's filled\r
35 #define OUTPUT_BUF_SIZE 4096\r
36 typedef struct\r
37 {\r
38         struct jpeg_destination_mgr pub;/* public fields */\r
39 \r
40         io::IWriteFile* file;           /* target file */\r
41         JOCTET buffer[OUTPUT_BUF_SIZE]; /* image buffer */\r
42 } mem_destination_mgr;\r
43 \r
44 \r
45 typedef mem_destination_mgr * mem_dest_ptr;\r
46 \r
47 \r
48 // init\r
49 static void jpeg_init_destination(j_compress_ptr cinfo)\r
50 {\r
51         mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;\r
52         dest->pub.next_output_byte = dest->buffer;\r
53         dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;\r
54 }\r
55 \r
56 \r
57 // flush to disk and reset buffer\r
58 static boolean jpeg_empty_output_buffer(j_compress_ptr cinfo)\r
59 {\r
60         mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;\r
61 \r
62         // for now just exit upon file error\r
63         if (dest->file->write(dest->buffer, OUTPUT_BUF_SIZE) != OUTPUT_BUF_SIZE)\r
64                 ERREXIT (cinfo, JERR_FILE_WRITE);\r
65 \r
66         dest->pub.next_output_byte = dest->buffer;\r
67         dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;\r
68 \r
69         return TRUE;\r
70 }\r
71 \r
72 \r
73 static void jpeg_term_destination(j_compress_ptr cinfo)\r
74 {\r
75         mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;\r
76         const size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;\r
77         // for now just exit upon file error\r
78         if (dest->file->write(dest->buffer, datacount) != datacount)\r
79                 ERREXIT (cinfo, JERR_FILE_WRITE);\r
80 }\r
81 \r
82 \r
83 // set up buffer data\r
84 static void jpeg_file_dest(j_compress_ptr cinfo, io::IWriteFile* file)\r
85 {\r
86         if (cinfo->dest == NULL)\r
87         { /* first time for this JPEG object? */\r
88                 cinfo->dest = (struct jpeg_destination_mgr *)\r
89                         (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo,\r
90                                                 JPOOL_PERMANENT,\r
91                                                 sizeof(mem_destination_mgr));\r
92         }\r
93 \r
94         mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;  /* for casting */\r
95 \r
96         /* Initialize method pointers */\r
97         dest->pub.init_destination = jpeg_init_destination;\r
98         dest->pub.empty_output_buffer = jpeg_empty_output_buffer;\r
99         dest->pub.term_destination = jpeg_term_destination;\r
100 \r
101         /* Initialize private member */\r
102         dest->file = file;\r
103 }\r
104 \r
105 \r
106 /* write_JPEG_memory: store JPEG compressed image into memory.\r
107 */\r
108 static bool writeJPEGFile(io::IWriteFile* file, IImage* image, u32 quality)\r
109 {\r
110         void (*format)(const void*, s32, void*) = 0;\r
111         switch( image->getColorFormat () )\r
112         {\r
113                 case ECF_R8G8B8:\r
114                         format = CColorConverter::convert_R8G8B8toR8G8B8;\r
115                         break;\r
116                 case ECF_A8R8G8B8:\r
117                         format = CColorConverter::convert_A8R8G8B8toR8G8B8;\r
118                         break;\r
119                 case ECF_A1R5G5B5:\r
120                         format = CColorConverter::convert_A1R5G5B5toB8G8R8;\r
121                         break;\r
122                 case ECF_R5G6B5:\r
123                         format = CColorConverter::convert_R5G6B5toR8G8B8;\r
124                         break;\r
125                 default:\r
126                         os::Printer::log("writeJPEGFile does not support image format", ColorFormatNames[image->getColorFormat()], ELL_WARNING);\r
127                         break;\r
128         }\r
129 \r
130         // couldn't find a color converter\r
131         if ( 0 == format )\r
132                 return false;\r
133 \r
134         const core::dimension2du dim = image->getDimension();\r
135 \r
136         struct jpeg_compress_struct cinfo;\r
137         struct jpeg_error_mgr jerr;\r
138         cinfo.err = jpeg_std_error(&jerr);\r
139 \r
140         jpeg_create_compress(&cinfo);\r
141         jpeg_file_dest(&cinfo, file);\r
142         cinfo.image_width = dim.Width;\r
143         cinfo.image_height = dim.Height;\r
144         cinfo.input_components = 3;\r
145         cinfo.in_color_space = JCS_RGB;\r
146 \r
147         jpeg_set_defaults(&cinfo);\r
148 \r
149         if ( 0 == quality )\r
150                 quality = 75;\r
151 \r
152         jpeg_set_quality(&cinfo, quality, TRUE);\r
153         jpeg_start_compress(&cinfo, TRUE);\r
154 \r
155         u8 * dest = new u8[dim.Width*3];\r
156 \r
157         if (dest)\r
158         {\r
159                 const u32 pitch = image->getPitch();\r
160                 JSAMPROW row_pointer[1];      /* pointer to JSAMPLE row[s] */\r
161                 row_pointer[0] = dest;\r
162 \r
163                 u8* src = (u8*)image->getData();\r
164 \r
165                 while (cinfo.next_scanline < cinfo.image_height)\r
166                 {\r
167                         // convert next line\r
168                         format( src, dim.Width, dest );\r
169                         src += pitch;\r
170                         jpeg_write_scanlines(&cinfo, row_pointer, 1);\r
171                 }\r
172 \r
173                 delete [] dest;\r
174 \r
175                 /* Step 6: Finish compression */\r
176                 jpeg_finish_compress(&cinfo);\r
177         }\r
178 \r
179         /* Step 7: Destroy */\r
180         jpeg_destroy_compress(&cinfo);\r
181 \r
182         return (dest != 0);\r
183 }\r
184 \r
185 \r
186 } // namespace video\r
187 } // namespace irr\r
188 \r
189 #endif // _IRR_COMPILE_WITH_LIBJPEG_\r
190 \r
191 namespace irr\r
192 {\r
193 namespace video\r
194 {\r
195 \r
196 IImageWriter* createImageWriterJPG()\r
197 {\r
198         return new CImageWriterJPG;\r
199 }\r
200 \r
201 CImageWriterJPG::CImageWriterJPG()\r
202 {\r
203 #ifdef _DEBUG\r
204         setDebugName("CImageWriterJPG");\r
205 #endif\r
206 }\r
207 \r
208 \r
209 bool CImageWriterJPG::isAWriteableFileExtension(const io::path& filename) const\r
210 {\r
211         return core::hasFileExtension ( filename, "jpg", "jpeg" );\r
212 }\r
213 \r
214 \r
215 bool CImageWriterJPG::writeImage(io::IWriteFile *file, IImage *image, u32 quality) const\r
216 {\r
217 #ifndef _IRR_COMPILE_WITH_LIBJPEG_\r
218         return false;\r
219 #else\r
220         return writeJPEGFile(file, image, quality);\r
221 #endif\r
222 }\r
223 \r
224 } // namespace video\r
225 } // namespace irr\r
226 \r
227 #endif\r
228 \r