ecere/gui/Window: Prevent uninitialized values if base Window methods not overridden...
[sdk] / ecere / src / gfx / bitmaps / JPEGFormat.ec
1 namespace gfx::bitmaps;
2
3 #define _Noreturn
4 import "Display"
5
6 #include <setjmp.h>
7 #include <stdio.h>
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 = 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 = (uint)(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 const 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.out_color_space == JCS_CMYK && cinfo.output_components == 4)
220                   {
221                      int cyan = 255-buffer[0][c*4+0];
222                      int m = 255-buffer[0][c*4+1];
223                      int y = 255-buffer[0][c*4+2];
224                      int k = 255-buffer[0][c*4+3];
225                      //picture[c] = ColorAlpha { 255, { cinfo.sample_range_limit[(255 - (cyan + k))], cinfo.sample_range_limit[(255 - (m + k))], cinfo.sample_range_limit[(255 - (y + k))] } };
226                      picture[c] = ColorCMYK { cyan * 100.0f / 255, m * 100.0f / 255, y * 100.0f / 255, k * 100.0f / 255 };
227                   }
228                   else if(cinfo.output_components == 1)
229                      picture[c] = ColorAlpha { 255, { buffer[0][c], buffer[0][c], buffer[0][c] } };
230                   else
231                      picture[c] = ColorAlpha { 255, { buffer[0][c*3], buffer[0][c*3+1], buffer[0][c*3+2] } };
232                }
233             }
234             result = true;
235          }
236          jpeg_finish_decompress(&cinfo);
237       }
238
239       jpeg_destroy_decompress(&cinfo);
240
241       if(!result)
242          bitmap.Free();
243       return result;
244    }
245
246    bool Save(Bitmap bitmap, const char *filename, void * options)
247    {
248       bool result = false;
249       if(bitmap.pixelFormat == pixelFormat888)
250       {
251          File f = FileOpen(filename, write);
252          if(f)
253          {
254             struct jpeg_compress_struct cinfo;
255             struct jpeg_error_mgr jerr;
256             JSAMPROW row;
257             ColorAlpha * picture = (ColorAlpha *)bitmap.picture;
258             byte * buffer;
259
260             cinfo.err = jpeg_std_error(&jerr);
261
262             jpeg_create_compress(&cinfo);
263             JPEG_SetDestination(&cinfo, f);
264
265             cinfo.image_width = bitmap.width;
266             cinfo.image_height = bitmap.height;
267             cinfo.input_components = 3;
268             cinfo.in_color_space = JCS_RGB;
269
270             jpeg_set_defaults(&cinfo);
271
272             jpeg_set_quality(&cinfo, 100, TRUE);
273
274             jpeg_start_compress(&cinfo, TRUE);
275
276             buffer = new byte[bitmap.width * 3];
277
278             for(;cinfo.next_scanline < cinfo.image_height;)
279             {
280                int c;
281                for(c = 0; c<bitmap.width; c++)
282                {
283                   if(cinfo.input_components == 1)
284                      buffer[c] = (picture[c].color.r + picture[c].color.g + picture[c].color.b) / 3;
285                   else
286                   {
287                      buffer[c*3]   = picture[c].color.r;
288                      buffer[c*3+1] = picture[c].color.g;
289                      buffer[c*3+2] = picture[c].color.b;
290                   }
291                }
292                row = buffer;
293                jpeg_write_scanlines(&cinfo, &row, 1);
294
295                picture += bitmap.stride;
296             }
297
298             delete buffer;
299
300             jpeg_finish_compress(&cinfo);
301             jpeg_destroy_compress(&cinfo);
302
303             delete f;
304             result = true;
305          }
306       }
307       return result;
308    }
309
310    ColorAlpha * LoadPalette(const char * fileName, const char * type)
311    {
312       ColorAlpha * result = null;
313       return result;
314    }
315 }