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