]> git.mxchange.org Git - simgear.git/blob - simgear/screen/jpgfactory.cxx
make headers include headers they depend on, don't rely on the c(xx)
[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 <plib/ssg.h>
29
30 #include "jpgfactory.hxx"
31    
32
33 #ifdef __cplusplus
34 extern "C" {
35 #endif
36
37 static void init_destination (j_compress_ptr cinfo);
38 static void term_destination (j_compress_ptr cinfo);
39 static boolean empty_output_buffer (j_compress_ptr cinfo);
40
41 #ifdef __cplusplus
42 }
43 #endif
44
45
46 typedef struct {
47     struct jpeg_destination_mgr pub; /* public fields */
48     unsigned char * outfile; /* target stream */
49     JOCTET * buffer;         /* start of buffer */
50     int numbytes;            /* num bytes used */
51     int maxsize;             /* size of outfile */
52     int error;               /* error flag */
53 } my_destination_mgr;
54
55 typedef my_destination_mgr * my_dest_ptr;
56
57 void (*jpgRenderFrame)(void) = NULL;
58
59 trJpgFactory::trJpgFactory() {
60     imageWidth = imageHeight = 0;
61     tile       = NULL;
62     buffer     = NULL;
63     IMAGE      = NULL;
64     tr         = NULL;
65     cinfo.dest = NULL;
66 }
67
68 trJpgFactory::~trJpgFactory() {
69     destroy();
70 }
71
72 /*
73  * deallocate our dynamic parts
74  */
75
76 void trJpgFactory::destroy( int error )
77 {
78     if( error )
79         printf( "!! Malloc Failure trJpgFactory ( %d )!!\n",
80                 error );
81
82     if( cinfo.dest )  jpeg_destroy_compress(&cinfo);
83     if( tr )          trDelete(tr);
84     if( IMAGE )       delete [] IMAGE;
85     if( buffer )      delete [] buffer;
86     if( tile )        delete [] tile;
87 }
88
89 /*
90  * allocate and initialize the jpeg compress struct
91  * application needs to dealocate this
92  */
93
94 int trJpgFactory::jpeg_init()
95 {
96     j_compress_ptr cinfoPtr = &cinfo;
97
98     cinfoPtr->err = jpeg_std_error(&jerr);
99     jpeg_create_compress(cinfoPtr);
100
101     /* following taken from jpeg library exaample code */
102     cinfoPtr->dest = (struct jpeg_destination_mgr *)
103                      (*cinfoPtr->mem->alloc_small)
104                      ((j_common_ptr)cinfoPtr,
105                       JPOOL_PERMANENT,
106                       sizeof(my_destination_mgr));
107
108     my_dest_ptr dest = (my_dest_ptr)cinfoPtr->dest;
109
110     if( !dest ) {
111         destroy(5);
112         return 5;
113     }
114
115     dest->pub.init_destination    = init_destination;
116     dest->pub.empty_output_buffer = empty_output_buffer;
117     dest->pub.term_destination    = term_destination;
118     dest->outfile  = NULL;
119     dest->numbytes = 0;
120     dest->maxsize  = 0;
121
122     cinfoPtr->image_width      = imageWidth;
123     cinfoPtr->image_height     = imageHeight;
124     cinfoPtr->input_components = 3;
125     cinfoPtr->in_color_space   = JCS_RGB;
126     jpeg_set_defaults(cinfoPtr);
127     jpeg_set_quality(cinfoPtr, (100 * 90) >> 8, TRUE);
128
129     return 0;
130 }
131
132
133 /*
134  * may also be used as reinit() to change image size
135  */
136
137 int trJpgFactory::init(int width, int height )
138 {
139     destroy();
140
141     if( width <= 0 || height <= 0 ) {
142         imageWidth  = DEFAULT_XS;
143         imageHeight = DEFAULT_YS;
144     } else {
145         imageWidth  = width;
146         imageHeight = height;
147     }
148
149     int bufsize = imageWidth * imageHeight * 3 * sizeof(GLubyte);
150
151     /* allocate buffer large enough to store one tile */
152     tile = new GLubyte[bufsize];
153     if (!tile) {
154         destroy(1);
155         return 1;
156     }
157
158     /* allocate buffer to hold a row of tiles */
159     buffer = new GLubyte[ bufsize ];
160     if (!buffer) {
161         destroy(2);
162         return 2;
163     }
164
165     /* this should be big enough */
166     IMAGESIZE = bufsize + 1024;
167     IMAGE = new unsigned char[ IMAGESIZE ];
168     if( !IMAGE ) {
169         destroy(3);
170         return 3;
171     }
172
173     tr = trNew();
174     if( !tr ) {
175         destroy(4);
176         return 4;
177     }
178     
179     trRowOrder(tr, TR_TOP_TO_BOTTOM);
180     trTileSize(tr, imageWidth, imageHeight, 0);
181     trImageSize(tr, imageWidth, imageHeight);
182     trTileBuffer(tr, GL_RGB, GL_UNSIGNED_BYTE, tile);
183
184     return jpeg_init();
185 }
186
187 /*
188  *compress the image
189  */
190
191 int trJpgFactory::compress()
192 {
193     JSAMPROW  row_pointer[1];
194     int       row_stride;
195
196     /* to keep track of jpeg library routines */
197     my_dest_ptr dest = (my_dest_ptr) cinfo.dest;
198
199     //printf("\tjpeg_start_compress(&cinfo, TRUE)\n");
200     jpeg_start_compress(&cinfo, TRUE);
201     if( !dest->error ) {
202         dest->outfile = IMAGE;
203         dest->maxsize = IMAGESIZE;
204         row_stride    = cinfo.image_width * 3;
205
206         while( cinfo.next_scanline < cinfo.image_height &&
207                !dest->error )
208         {
209             row_pointer[0] = buffer + (cinfo.next_scanline * row_stride);
210             jpeg_write_scanlines(&cinfo, row_pointer, 1);
211         }
212     }
213     if( !dest->error ) {
214         // printf("\n\tBEFORE: jpeg_finish_compress(&cinfo)\n");
215         jpeg_finish_compress(&cinfo);
216         // printf("\tAFTER: jpeg_finish_compress(&cinfo)\n");
217     } else {
218         printf("INTERNAL JPEG_FACTORY ERROR\n");
219         jpeg_abort_compress(&cinfo);
220         /* OK - I am paranoid */
221         dest->numbytes = 0;
222     }
223     return dest->numbytes;
224 }
225
226 /*
227  * Makes the image then calls compress()
228  */
229
230 int trJpgFactory::render()
231 {
232     if( !tr || !jpgRenderFrame ) {
233         printf("!! NO tr !!\n   trJpgFactory::render()\n");
234         return 0;
235     }
236
237     // Make sure we have SSG projection primed for current view
238     glMatrixMode(GL_MODELVIEW);
239     glLoadIdentity();
240
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