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