]> git.mxchange.org Git - simgear.git/blob - simgear/screen/jpgfactory.cxx
Patch from Melchior Franz:
[simgear.git] / simgear / screen / jpgfactory.cxx
1 // jpgfactory.cxx -- jpeg frontend for TR library
2 //
3 // Written by Norman Vine, started August 2001.
4 //
5 // Copyright (C) 2001  Norman Vine - nhv@yahoo.com
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 // $Id:
22
23
24 #ifdef HAVE_WINDOWS_H
25 #  include <windows.h>
26 #endif
27
28 #include <stdlib.h>
29 #include <stdio.h>
30
31 #include <plib/ssg.h>
32
33 #include "jpgfactory.hxx"
34    
35
36 #ifdef __cplusplus
37 extern "C" {
38 #endif
39
40 static void init_destination (j_compress_ptr cinfo);
41 static void term_destination (j_compress_ptr cinfo);
42 static boolean empty_output_buffer (j_compress_ptr cinfo);
43
44 #ifdef __cplusplus
45 }
46 #endif
47
48
49 typedef struct {
50     struct jpeg_destination_mgr pub; /* public fields */
51     unsigned char * outfile; /* target stream */
52     JOCTET * buffer;         /* start of buffer */
53     int numbytes;            /* num bytes used */
54     int maxsize;             /* size of outfile */
55     int error;               /* error flag */
56 } my_destination_mgr;
57
58 typedef my_destination_mgr * my_dest_ptr;
59
60 /* Where should this go <simgear/screen/tr.h> ?? */
61 extern void trRenderFrame( void );
62
63 trJpgFactory::trJpgFactory() {
64     imageWidth = imageHeight = 0;
65     tile       = NULL;
66     buffer     = NULL;
67     IMAGE      = NULL;
68     tr         = NULL;
69     cinfo.dest = NULL;
70 }
71
72 trJpgFactory::~trJpgFactory() {
73     destroy();
74 }
75
76 /*
77  * deallocate our dynamic parts
78  */
79
80 void trJpgFactory::destroy( int error )
81 {
82     if( error )
83         printf( "!! Malloc Failure trJpgFactory ( %d )!!\n",
84                 error );
85
86     if( cinfo.dest )  jpeg_destroy_compress(&cinfo);
87     if( tr )          trDelete(tr);
88     if( IMAGE )       delete [] IMAGE;
89     if( buffer )      delete [] buffer;
90     if( tile )        delete [] tile;
91 }
92
93 /*
94  * allocate and initialize the jpeg compress struct
95  * application needs to dealocate this
96  */
97
98 int trJpgFactory::jpeg_init()
99 {
100     j_compress_ptr cinfoPtr = &cinfo;
101
102     cinfoPtr->err = jpeg_std_error(&jerr);
103     jpeg_create_compress(cinfoPtr);
104
105     /* following taken from jpeg library exaample code */
106     cinfoPtr->dest = (struct jpeg_destination_mgr *)
107                      (*cinfoPtr->mem->alloc_small)
108                      ((j_common_ptr)cinfoPtr,
109                       JPOOL_PERMANENT,
110                       sizeof(my_destination_mgr));
111
112     my_dest_ptr dest = (my_dest_ptr)cinfoPtr->dest;
113
114     if( !dest ) {
115         destroy(5);
116         return 5;
117     }
118
119     dest->pub.init_destination    = init_destination;
120     dest->pub.empty_output_buffer = empty_output_buffer;
121     dest->pub.term_destination    = term_destination;
122     dest->outfile  = NULL;
123     dest->numbytes = 0;
124     dest->maxsize  = 0;
125
126     cinfoPtr->image_width      = imageWidth;
127     cinfoPtr->image_height     = imageHeight;
128     cinfoPtr->input_components = 3;
129     cinfoPtr->in_color_space   = JCS_RGB;
130     jpeg_set_defaults(cinfoPtr);
131     jpeg_set_quality(cinfoPtr, (100 * 90) >> 8, TRUE);
132
133     return 0;
134 }
135
136
137 /*
138  * may also be used as reinit() to change image size
139  */
140
141 int trJpgFactory::init(int width, int height )
142 {
143     destroy();
144
145     if( width <= 0 || height <= 0 ) {
146         imageWidth  = DEFAULT_XS;
147         imageHeight = DEFAULT_YS;
148     } else {
149         imageWidth  = width;
150         imageHeight = height;
151     }
152
153     int bufsize = imageWidth * imageHeight * 3 * sizeof(GLubyte);
154
155     /* allocate buffer large enough to store one tile */
156     tile = new GLubyte[bufsize];
157     if (!tile) {
158         destroy(1);
159         return 1;
160     }
161
162     /* allocate buffer to hold a row of tiles */
163     buffer = new GLubyte[ bufsize ];
164     if (!buffer) {
165         destroy(2);
166         return 2;
167     }
168
169     /* this should be big enough */
170     IMAGESIZE = bufsize + 1024;
171     IMAGE = new unsigned char[ IMAGESIZE ];
172     if( !IMAGE ) {
173         destroy(3);
174         return 3;
175     }
176
177     tr = trNew();
178     if( !tr ) {
179         destroy(4);
180         return 4;
181     }
182     
183     trRowOrder(tr, TR_TOP_TO_BOTTOM);
184     trTileSize(tr, imageWidth, imageHeight, 0);
185     trImageSize(tr, imageWidth, imageHeight);
186     trTileBuffer(tr, GL_RGB, GL_UNSIGNED_BYTE, tile);
187
188     return jpeg_init();
189 }
190
191 /*
192  *compress the image
193  */
194
195 int trJpgFactory::compress()
196 {
197     JSAMPROW  row_pointer[1];
198     int       row_stride;
199
200     /* to keep track of jpeg library routines */
201     my_dest_ptr dest = (my_dest_ptr) cinfo.dest;
202
203     //printf("\tjpeg_start_compress(&cinfo, TRUE)\n");
204     jpeg_start_compress(&cinfo, TRUE);
205     if( !dest->error ) {
206         dest->outfile = IMAGE;
207         dest->maxsize = IMAGESIZE;
208         row_stride    = cinfo.image_width * 3;
209
210         while( cinfo.next_scanline < cinfo.image_height &&
211                !dest->error )
212         {
213             row_pointer[0] = buffer + (cinfo.next_scanline * row_stride);
214             jpeg_write_scanlines(&cinfo, row_pointer, 1);
215         }
216     }
217     if( !dest->error ) {
218         // printf("\n\tBEFORE: jpeg_finish_compress(&cinfo)\n");
219         jpeg_finish_compress(&cinfo);
220         // printf("\tAFTER: jpeg_finish_compress(&cinfo)\n");
221     } else {
222         printf("INTERNAL JPEG_FACTORY ERROR\n");
223         jpeg_abort_compress(&cinfo);
224         /* OK - I am paranoid */
225         dest->numbytes = 0;
226     }
227     return dest->numbytes;
228 }
229
230 /*
231  * Makes the image then calls compress()
232  */
233
234 int trJpgFactory::render()
235 {
236     if( !tr ) {
237         printf("!! NO tr !!\n   trJpgFactory::render()\n");
238         return 0;
239     }
240
241     // Make sure we have SSG projection primed for current view
242     glMatrixMode(GL_MODELVIEW);
243     glLoadIdentity();
244
245     sgFrustum *frustum = ssgGetFrustum();
246     trFrustum(tr,
247               frustum->getLeft(), frustum->getRight(),
248               frustum->getBot(),  frustum->getTop(), 
249               frustum->getNear(), frustum->getFar());
250
251     /* just to be safe... */
252     glPixelStorei(GL_PACK_ALIGNMENT, 1);
253
254     // printf("\ttrBeginTile(tr)\n");
255     trBeginTile(tr);
256     trRenderFrame();
257     trEndTile(tr);
258
259     /* just to be safe */
260     int curTileHeight = trGet(tr, TR_CURRENT_TILE_HEIGHT);
261     int curTileWidth  = trGet(tr, TR_CURRENT_TILE_WIDTH);
262
263     /* reverse image top to bottom */
264     int bytesPerImageRow = imageWidth * 3*sizeof(GLubyte);
265     int bytesPerTileRow  = imageWidth * 3*sizeof(GLubyte);
266     int bytesPerCurrentTileRow = (curTileWidth) * 3*sizeof(GLubyte);
267     int i;
268     for (i=0;i<imageHeight;i++) {
269         memcpy(buffer + (curTileHeight-1-i) * bytesPerImageRow, /* Dest */
270                tile + i*bytesPerTileRow,              /* Src */
271                bytesPerCurrentTileRow);               /* Byte count*/
272     }
273
274     //  printf("exit trJpgFactory::render()\n");
275     return  compress();
276 }
277
278
279 #define OUTPUT_BUF_SIZE  4096
280
281 #ifdef __cplusplus
282 extern "C" {
283 #endif
284
285 /*
286  * Initialize destination --- called by jpeg_start_compress
287  * before any data is actually written.
288  */
289
290 static void init_destination (j_compress_ptr cinfo)
291 {
292     // printf("enter init_destination()\n");
293     my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
294
295     /* following taken from jpeg library exaample code
296      * Allocate the output buffer ---
297      * it automaically will be released when done with image */
298         
299     dest->buffer = (JOCTET *)(*cinfo->mem->alloc_small)
300                    ((j_common_ptr)cinfo, JPOOL_IMAGE,
301                     OUTPUT_BUF_SIZE * sizeof(JOCTET) );
302
303     if( !dest->buffer ) {
304         printf("MALLOC FAILED jpegFactory init_destination()\n");
305         dest->error = TRUE;
306     } else {
307         dest->error = FALSE;
308     }
309     dest->pub.next_output_byte = dest->buffer;
310     dest->pub.free_in_buffer   = OUTPUT_BUF_SIZE;
311     dest->numbytes = 0;
312 }
313
314 /*
315  * Empty the output buffer --- called whenever buffer fills up.
316  */
317
318 static boolean empty_output_buffer (j_compress_ptr cinfo)
319 {
320     // printf("enter empty_output_buffer(%d)\n", OUTPUT_BUF_SIZE);
321     my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
322
323     if( (!dest->error) &&
324           ((dest->numbytes + OUTPUT_BUF_SIZE) < dest->maxsize) )
325     {
326         memcpy( dest->outfile+dest->numbytes, dest->buffer, (size_t)OUTPUT_BUF_SIZE);
327
328         dest->pub.next_output_byte = dest->buffer;
329         dest->pub.free_in_buffer   = OUTPUT_BUF_SIZE;
330
331         dest->numbytes += OUTPUT_BUF_SIZE;
332     } else {
333         printf("BUFFER OVERFLOW jpegFactory empty_output_buffer()\n");
334         dest->numbytes = 0;
335         dest->error    = TRUE;
336     }
337     return TRUE;
338 }
339
340 /*
341  * Terminate destination --- called by jpeg_finish_compress
342  * after all data has been written.  Usually needs to flush buffer.
343  *
344  * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
345  * application must deal with any cleanup that should happen even
346  * for error exit.
347  */
348
349 static void term_destination (j_compress_ptr cinfo)
350 {
351     my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
352
353     size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
354
355     if( (!dest->error) &&
356           ((dest->numbytes + datacount) < (unsigned int)dest->maxsize) )
357     {
358         memcpy( dest->outfile+dest->numbytes, dest->buffer, datacount);
359         dest->numbytes += datacount;
360     } else {
361         printf("BUFFER OVERFLOW jpegFactory term_destination()\n");
362         dest->numbytes = 0;
363         dest->error = TRUE;
364     }
365     // printf(" term_destination(%d) total %d\n", datacount, dest->numbytes );
366 }
367
368 #ifdef __cplusplus
369 }
370 #endif
371