root/lang/c/SDL_image_save/trunk/IMGS_jpg.c @ 29324

Revision 29324, 9.6 kB (checked in by cielacanth, 4 years ago)

Initial import.

Line 
1/*
2    SDL_image_save:  An example image saving library for use with SDL
3    Copyright (C) 2005 cielacanth. All rights reserved.
4
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public
7    License as published by the Free Software Foundation; either
8    version 2 of the License, or (at your option) any later version.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with this library; if not, write to the Free
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
19    cielacanth
20    cielacanth@s60.xrea.com
21*/
22
23/* A simple library to save images of various formats as SDL surfaces */
24
25#include "SDL_image_save.h"
26
27#include <setjmp.h>
28#include "libjpeg/jpeglib.h"
29
30/* This code assumes that sizeof(JSAMPLE) is 8 bit */
31#if BITS_IN_JSAMPLE != 8
32#error "IMGS_jpg : this code assumes that sizeof(JSAMPLE) is 8 bit"
33#endif
34
35/* Buffering size */
36#define OUTPUT_BUFFER_SIZE      4096
37
38/* Image destination struct */
39struct imgs_destination_mgr {
40        struct jpeg_destination_mgr pub;
41
42        SDL_RWops *ops;
43        Uint8 buffer[OUTPUT_BUFFER_SIZE];
44};
45
46/* Initialize destination, called by jpeg_start_compress
47 * before any data is actually written.
48 */
49static void jpg_init_destination(j_compress_ptr cinfo)
50{
51        struct imgs_destination_mgr *dest = (struct imgs_destination_mgr *) cinfo->dest;
52        dest->pub.next_output_byte      = dest->buffer;
53        dest->pub.free_in_buffer        = OUTPUT_BUFFER_SIZE;
54}
55
56/* Write JPEG bytes to a data stream */
57static boolean jpg_empty_output_buffer(j_compress_ptr cinfo)
58{
59        struct imgs_destination_mgr *dest = (struct imgs_destination_mgr *) cinfo->dest;
60        int size;
61
62        /* Output data to data stream */
63        size = SDL_RWwrite(dest->ops, dest->buffer, 1, OUTPUT_BUFFER_SIZE);
64        if (size != OUTPUT_BUFFER_SIZE) {
65                /* Error has occured while writing a data */
66                IMGS_SetError("IMGS_jpg.c: Error has occured while writing a data");
67                return FALSE;
68        }
69
70        /* Reset buffer */
71        dest->pub.next_output_byte = dest->buffer;
72        dest->pub.free_in_buffer = OUTPUT_BUFFER_SIZE;
73        return TRUE;
74}
75
76/* Terminate destination, called by jpeg_finish_compress
77 * after all data has been written.
78 */
79static void jpg_term_destination(j_compress_ptr cinfo)
80{
81        struct imgs_destination_mgr *dest = (struct imgs_destination_mgr *) cinfo->dest;
82        int size = OUTPUT_BUFFER_SIZE - dest->pub.free_in_buffer;
83
84        /* Output data to data stream */
85        SDL_RWwrite(dest->ops, dest->buffer, 1, size);
86}
87
88/* Error handler struct */
89struct imgs_error_mgr {
90        struct jpeg_error_mgr err;
91        jmp_buf escape;
92};
93
94/* Called if error has occured */
95static void jpg_error_exit(j_common_ptr cinfo)
96{
97        struct imgs_error_mgr *err = (struct imgs_error_mgr *)cinfo->err;
98        longjmp(err->escape, 1);
99}
100
101/* Output no message */
102static void jpg_output_no_message(j_common_ptr cinfo)
103{
104        /* do nothing */
105}
106
107/* Return the number of a true bit in a bit mask */
108static int jpg_get_number_of_bits(Uint32 bit_mask)
109{
110        int count = 0;
111
112        while ( bit_mask != 0 ) {
113                bit_mask &= bit_mask - 1;
114                ++count;
115        }
116        return count;
117}
118
119/* Make a colormap.
120 * Colormap is a convert table of one color channel
121 * from bits bpp to 8bpp.
122 */
123static void jpg_make_colormap(int bits, JSAMPROW colormap)
124{
125        int i_max = (1 << bits) - 1;
126        int i;
127
128        if ( i_max == 0 ) return;
129
130        for ( i = 0; i <= i_max; ++i ) {
131                float val = 255.0f * (float)i / i_max;
132
133                /* Add 0.5 to round off */
134                int c = (int) (val + 0.5f);
135                if ( c > 255 ) c = 255;
136                colormap[i] = c;
137        }
138}
139
140/* Save a SDL surface to a source data */
141int IMGS_SaveJPG_RW(SDL_Surface* surface, SDL_RWops* ops)
142{
143        struct jpeg_compress_struct     cinfo;
144        struct imgs_error_mgr           jerr;
145
146        const SDL_PixelFormat *fmt      = surface->format;
147        const Uint32 surface_pitch      = surface->pitch;
148        const Uint32 width      = surface->w;
149        const Uint32 height     = surface->h;
150        const Uint32 Rmask      = fmt->Rmask;
151        const Uint32 Gmask      = fmt->Gmask;
152        const Uint32 Bmask      = fmt->Bmask;
153        const Uint32 Amask      = fmt->Amask;
154        const Uint32 Rshift     = fmt->Rshift;
155        const Uint32 Gshift     = fmt->Gshift;
156        const Uint32 Bshift     = fmt->Bshift;
157        const Uint32 Ashift     = fmt->Ashift;
158
159        JSAMPROW        row_pointer = NULL;
160        Uint32          row_bytes;
161        int                     error_code = 0;
162
163        /* Check whether opt is valid or not */
164        if ( ops == NULL ) {
165                /* The error message has been set in SDL_RWFromFile */
166                return 1;
167        }
168
169        /* Initialize error struct */
170        cinfo.err = jpeg_std_error(&jerr.err);
171        jerr.err.error_exit             = jpg_error_exit;
172        jerr.err.output_message = jpg_output_no_message;
173
174        /* Set error handler */
175        if( setjmp(jerr.escape) != 0 ) {
176                /* If we get here, libjpeg found an error */
177                IMGS_SetError("IMGS_jpg.c: JPEG loading error");
178                error_code = 2;
179                goto done;
180        }
181
182        /* Create a JPEG struct */
183        jpeg_create_compress(&cinfo);
184
185        /* Setup image header */
186        cinfo.image_width               = width;
187        cinfo.image_height              = height;
188        cinfo.input_components  = 3;
189        cinfo.in_color_space    = JCS_RGB;
190
191        /* Setup cinfo by default parameters */
192        jpeg_set_defaults(&cinfo);
193
194        /* Ranges of quality are 1...100,  default is 75. */
195        jpeg_set_quality(&cinfo, 75, TRUE /* force base_line */);
196
197        /* Allocate jpeg_destination_mgr in a JPEG struct */
198        cinfo.dest = (struct jpeg_destination_mgr *)
199                (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT,
200                                                                   sizeof(struct imgs_destination_mgr));
201
202        if ( cinfo.dest == NULL ) {
203                IMGS_SetError("IMGS_jpg.c: Out of memory");
204                error_code = 5;
205                goto done;
206        }
207        else {
208                /* Initialize jpeg_destination_mgr in a JPEG struct */
209                struct imgs_destination_mgr     *dest;
210
211                dest = (struct imgs_destination_mgr *) cinfo.dest;
212                dest->pub.init_destination              = jpg_init_destination;
213                dest->pub.term_destination              = jpg_term_destination;
214                dest->pub.empty_output_buffer   = jpg_empty_output_buffer;
215                dest->ops = ops;
216                dest->pub.free_in_buffer = 0; /* forces empty_output_buffer on first read */
217                dest->pub.next_output_byte = NULL; /* until buffer loaded */
218        }
219
220        /* Allocate a row memory to convert data representation.
221         * Libjpeg excepts RGB888 pixel format.
222         */
223        row_bytes       = width * 3;
224        row_pointer     = (JSAMPROW)
225                (*cinfo.mem->alloc_large)((j_common_ptr) &cinfo, JPOOL_PERMANENT, row_bytes);
226        if ( row_pointer == NULL ) {
227                IMGS_SetError("IMGS_jpg.c: Out of memory");
228                error_code = 6;
229                goto done;
230        }
231
232        /* Start compresion */
233        jpeg_start_compress(&cinfo, TRUE);
234
235        /* Libjpeg excepts RGB888 pixel format */
236        switch ( fmt->BytesPerPixel ) {
237        case 1: {
238                SDL_Palette *palette = fmt->palette;
239                Uint8           *pixels = (Uint8 *) surface->pixels;
240
241                while ( cinfo.next_scanline < height ) {
242                        Uint32 x;
243                        for ( x = 0; x < width; ++x ) {
244                                SDL_Color *col = &palette->colors[pixels[x]];
245                                row_pointer[x * 3 + 0] = col->r;
246                                row_pointer[x * 3 + 1] = col->g;
247                                row_pointer[x * 3 + 2] = col->b;
248                        }
249                        pixels += surface_pitch;
250
251                        /* Write data */
252                        jpeg_write_scanlines(&cinfo, &row_pointer, 1);
253                }
254                }
255                break;
256
257        case 2: {
258                Uint8 *pixels = (Uint8 *) surface->pixels;
259                JSAMPLE colormap[3][256];       /* colormap for rgb */
260
261                /* Setup colormap for each rgb component */
262                jpg_make_colormap(jpg_get_number_of_bits(Rmask), colormap[0]);
263                jpg_make_colormap(jpg_get_number_of_bits(Gmask), colormap[1]);
264                jpg_make_colormap(jpg_get_number_of_bits(Bmask), colormap[2]);
265
266                while ( cinfo.next_scanline < height ) {
267                        Uint32 x;
268                        for ( x = 0; x < width; ++x ) {
269                                Uint16 c = *(Uint16 *) &pixels[x * 2];
270                                row_pointer[x * 3 + 0] = colormap[0][(c & Rmask) >> Rshift];
271                                row_pointer[x * 3 + 1] = colormap[1][(c & Gmask) >> Gshift];
272                                row_pointer[x * 3 + 2] = colormap[2][(c & Bmask) >> Bshift];
273                        }
274                        pixels += surface_pitch;
275
276                        /* Write data */
277                        jpeg_write_scanlines(&cinfo, &row_pointer, 1);
278                }
279                }
280                break;
281
282        case 3: {
283                Uint8 *pixels = (Uint8 *) surface->pixels;
284
285                /* Pixel format for 24bpp is usually RRGGBB or BBGGRR. */
286                if ( Rmask == 0x0000FF && Gmask == 0x00FF00 && Bmask == 0xFF0000 ) {
287                        while ( cinfo.next_scanline < height ) {
288                                /* Write data directly */
289                                jpeg_write_scanlines(&cinfo, &pixels, 1);
290                                pixels += surface_pitch;
291                        }
292                }
293                else {
294                        while ( cinfo.next_scanline < height ) {
295                                Uint32 x;
296                                for ( x = 0; x < row_bytes; x += 3 ) {
297                                        Uint32 c = (pixels[x + 2] << 16) | (pixels[x + 1] << 8) | (pixels[x + 0] << 0);
298                                        row_pointer[x + 0] = (c & Rmask) >> Rshift;
299                                        row_pointer[x + 1] = (c & Gmask) >> Gshift;
300                                        row_pointer[x + 2] = (c & Bmask) >> Bshift;
301                                }
302                                pixels += surface_pitch;
303
304                                /* Write data */
305                                jpeg_write_scanlines(&cinfo, &row_pointer, 1);
306                        }
307                }
308                }
309                break;
310
311        case 4: {
312                Uint8 *pixels = (Uint8 *) surface->pixels;
313
314                while ( cinfo.next_scanline < height ) {
315                        Uint32 x;
316                        for ( x = 0; x < width; ++x ) {
317                                Uint32 c = *(Uint32 *) &pixels[x * 4];
318                                row_pointer[x * 3 + 0] = (c & Rmask) >> Rshift;
319                                row_pointer[x * 3 + 1] = (c & Gmask) >> Gshift;
320                                row_pointer[x * 3 + 2] = (c & Bmask) >> Bshift;
321                        }
322                        pixels += surface_pitch;
323
324                        /* Write data */
325                        jpeg_write_scanlines(&cinfo, &row_pointer, 1);
326                }
327                }
328                break;
329
330        default:
331                IMGS_SetError("IMGS_jpg.c: Unknown pixel format");
332                error_code = 8;
333                goto done;
334                break;
335        }
336
337        /* Finish compression */
338        jpeg_finish_compress(&cinfo);
339
340done:; /* Clean up code */
341
342        /* This is an impotant step since it will release a good deal of memory */
343        jpeg_destroy_compress(&cinfo);
344
345        return error_code;
346}
Note: See TracBrowser for help on using the browser.