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