43617f799a09c06ca16200a4493aa28470ab63e7
[sdk] / ecere / src / gfx / bitmaps / JPEGFormat.ec
1 namespace gfx::bitmaps;
2
3 import "Display"
4
5 #include <setjmp.h>
6
7 typedef uintptr size_t; // For now, until we add uintsize
8
9 #include "jpeglib.h"
10 #include "jerror.h"
11
12 // ERROR HANDLER
13 typedef struct
14 {
15    struct jpeg_error_mgr jpeg;
16    jmp_buf setjmpBuffer;
17 } ErrorHandler;
18
19 static void JPEG_ExitHandler(j_common_ptr cinfo)
20 {
21    longjmp(((ErrorHandler *) cinfo->err)->setjmpBuffer, 1);
22 }
23
24 // DATA SOURCE
25 typedef struct
26 {
27    struct jpeg_source_mgr pub;
28    
29    File infile;
30    byte * buffer;
31    boolean startOfFile;
32 } SourceManager;
33
34 #define INPUT_BUF_SIZE  4096
35
36 static void JPEG_InitSource (j_decompress_ptr cinfo)
37 {
38    SourceManager * src = (SourceManager *) cinfo->src;
39    src->startOfFile = TRUE;
40 }
41
42 static boolean JPEG_FillInputBuffer (j_decompress_ptr cinfo)
43 {
44    SourceManager * src = (SourceManager *) cinfo->src;
45    uint nbytes = src->infile.Read(src->buffer, sizeof(byte), INPUT_BUF_SIZE);
46    if(nbytes <= 0)
47    {
48       if(src->startOfFile)
49          ERREXIT(cinfo, JERR_INPUT_EMPTY);
50       WARNMS(cinfo, JWRN_JPEG_EOF);
51       
52       src->buffer[0] = (byte) 0xFF;
53       src->buffer[1] = (byte) JPEG_EOI;
54       nbytes = 2;
55    }
56    
57    src->pub.next_input_byte = src->buffer;
58    src->pub.bytes_in_buffer = (size_t)nbytes;
59    src->startOfFile = FALSE;
60    
61    return TRUE;
62 }
63
64 static void JPEG_SkipInputData (j_decompress_ptr cinfo, long num_bytes)
65 {
66    SourceManager * src = (SourceManager *) cinfo->src;
67    
68    if (num_bytes > 0) 
69    {
70       while (num_bytes > (long) src->pub.bytes_in_buffer)
71       {
72          num_bytes -= (long) src->pub.bytes_in_buffer;
73          JPEG_FillInputBuffer(cinfo);
74       }
75       src->pub.next_input_byte += (uint) num_bytes;
76       src->pub.bytes_in_buffer -= (uint) num_bytes;
77    }
78 }
79
80 static void JPEG_TermSource(j_decompress_ptr cinfo)
81 {
82    
83 }
84
85 static void JPEG_SetSource(j_decompress_ptr cinfo, File infile)
86 {
87    SourceManager * src;
88    
89    if (!cinfo->src)
90    {
91       cinfo->src = (struct jpeg_source_mgr *)
92          (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
93          sizeof(SourceManager));
94       src = (SourceManager *) cinfo->src;
95       src->buffer = (byte *)
96          (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
97          INPUT_BUF_SIZE * sizeof(byte));
98    }
99    
100    src = (SourceManager *) cinfo->src;
101    src->pub.init_source = JPEG_InitSource;
102    src->pub.fill_input_buffer = JPEG_FillInputBuffer;
103    src->pub.skip_input_data = JPEG_SkipInputData;
104    src->pub.term_source = JPEG_TermSource;
105    src->pub.resync_to_restart = jpeg_resync_to_restart;
106    src->infile = infile;
107    src->pub.bytes_in_buffer = 0;
108    src->pub.next_input_byte = null;
109 }
110
111 // DATA DESTINATION
112 typedef struct
113 {
114    struct jpeg_destination_mgr pub;
115    
116    File outfile;
117    byte * buffer;
118 } DestinationManager;
119
120 #define OUTPUT_BUF_SIZE  4096
121
122 static void JPEG_InitDestination(j_compress_ptr cinfo)
123 {
124    DestinationManager * dest = (DestinationManager * ) cinfo->dest;
125    
126    dest->buffer = (byte *)
127       (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
128                                   OUTPUT_BUF_SIZE * sizeof(byte));
129    
130    dest->pub.next_output_byte = dest->buffer;
131    dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
132 }
133
134 static boolean JPEG_EmptyOutputBuffer(j_compress_ptr cinfo)
135 {
136    DestinationManager * dest = (DestinationManager *) cinfo->dest;
137    
138    if (dest->outfile.Write(dest->buffer, sizeof(byte), OUTPUT_BUF_SIZE) !=
139       (uint) OUTPUT_BUF_SIZE)
140       ERREXIT(cinfo, JERR_FILE_WRITE);
141    
142    dest->pub.next_output_byte = dest->buffer;
143    dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
144    
145    return TRUE;
146 }
147
148 static void JPEG_TermDestination(j_compress_ptr cinfo)
149 {
150    DestinationManager * dest = (DestinationManager *) cinfo->dest;
151    uint datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
152    
153    if (datacount > 0)
154    {
155       if (dest->outfile.Write(dest->buffer, sizeof(byte), datacount) != datacount)
156          ERREXIT(cinfo, JERR_FILE_WRITE);
157    }
158 /*
159    fflush(dest->outfile);
160    if (ferror(dest->outfile)) ERREXIT(cinfo, JERR_FILE_WRITE);
161 */
162 }
163
164 static void JPEG_SetDestination(j_compress_ptr cinfo, File outfile)
165 {
166    DestinationManager * dest;
167    
168    if(!cinfo->dest) 
169    {
170       cinfo->dest = (struct jpeg_destination_mgr *)
171          (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
172          sizeof(DestinationManager));
173    }
174    
175    dest = (DestinationManager *) cinfo->dest;
176    dest->pub.init_destination = JPEG_InitDestination;
177    dest->pub.empty_output_buffer = JPEG_EmptyOutputBuffer;
178    dest->pub.term_destination = JPEG_TermDestination;
179    dest->outfile = outfile;
180 }
181
182 // BITMAP DRIVER
183 static char * extensions[] = { "jpg", "jpeg", null };
184
185 class JPGFormat : BitmapFormat
186 {
187    class_property(extensions) = extensions;
188
189    bool Load(Bitmap bitmap, File f)
190    {
191       bool result = false;
192       struct jpeg_decompress_struct cinfo;
193
194       ErrorHandler handler;
195       cinfo.err = jpeg_std_error(&handler.jpeg);
196       handler.jpeg.error_exit = JPEG_ExitHandler;
197       if(!setjmp(handler.setjmpBuffer))
198       {
199          jpeg_create_decompress(&cinfo);
200
201          JPEG_SetSource(&cinfo, f);
202
203          jpeg_read_header(&cinfo, TRUE);
204
205          if(bitmap.Allocate(null, cinfo.image_width, cinfo.image_height, 0, pixelFormat888, false))
206          {
207             JSAMPARRAY buffer;
208             ColorAlpha * picture;
209
210             jpeg_start_decompress(&cinfo);
211             buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE, cinfo.output_width * cinfo.output_components, 1);
212
213             for(picture = (ColorAlpha *)bitmap.picture; cinfo.output_scanline < cinfo.output_height; picture += bitmap.stride) 
214             {
215                int c;
216                jpeg_read_scanlines(&cinfo, buffer, 1);
217                for(c = 0; c<cinfo.image_width; c++)
218                {
219                   if(cinfo.output_components == 1)
220                      picture[c] = ColorAlpha { 255, { buffer[0][c], buffer[0][c], buffer[0][c] } };
221                   else
222                      picture[c] = ColorAlpha { 255, { buffer[0][c*3], buffer[0][c*3+1], buffer[0][c*3+2] } };
223                }
224             }
225             result = true;
226          }
227          jpeg_finish_decompress(&cinfo);
228       }
229
230       jpeg_destroy_decompress(&cinfo);
231
232       if(!result)
233          bitmap.Free();
234       return result;
235    }
236
237    bool Save(Bitmap bitmap, char *filename, void * options)
238    {
239       bool result = false;
240       if(bitmap.pixelFormat == pixelFormat888)
241       {
242          File f = FileOpen(filename, write);
243          if(f) 
244          {
245             struct jpeg_compress_struct cinfo;
246             struct jpeg_error_mgr jerr;
247             JSAMPROW row;
248             ColorAlpha * picture = (ColorAlpha *)bitmap.picture;
249             byte * buffer;
250
251             cinfo.err = jpeg_std_error(&jerr);
252
253             jpeg_create_compress(&cinfo);
254             JPEG_SetDestination(&cinfo, f);
255
256             cinfo.image_width = bitmap.width;
257             cinfo.image_height = bitmap.height;
258             cinfo.input_components = 3;
259             cinfo.in_color_space = JCS_RGB;
260
261             jpeg_set_defaults(&cinfo);
262      
263             jpeg_set_quality(&cinfo, 100, TRUE);
264
265             jpeg_start_compress(&cinfo, TRUE);
266
267             buffer = new byte[bitmap.width * 3];
268
269             for(;cinfo.next_scanline < cinfo.image_height;)
270             {
271                int c;
272                for(c = 0; c<bitmap.width; c++)
273                {
274                   if(cinfo.input_components == 1)
275                      buffer[c] = (picture[c].color.r + picture[c].color.g + picture[c].color.b) / 3;
276                   else
277                   {
278                      buffer[c*3]   = picture[c].color.r;
279                      buffer[c*3+1] = picture[c].color.g;
280                      buffer[c*3+2] = picture[c].color.b;
281                   }
282                }
283                row = buffer;
284                jpeg_write_scanlines(&cinfo, &row, 1);
285
286                picture += bitmap.stride;
287             }
288
289             delete buffer;
290
291             jpeg_finish_compress(&cinfo);
292             jpeg_destroy_compress(&cinfo);
293
294             delete f;
295             result = true;
296          }      
297       }
298       return result;   
299    }
300
301    ColorAlpha * LoadPalette(char * fileName, char * type)
302    {
303       ColorAlpha * result = null;
304       return result;
305    }
306 }