FreeWRL / FreeX3D 4.3.0
Snapshot.c
1/*
2
3
4CProto ???
5
6*/
7
8
9/****************************************************************************
10 This file is part of the FreeWRL/FreeX3D Distribution.
11
12 Copyright 2009 CRC Canada. (http://www.crc.gc.ca)
13
14 FreeWRL/FreeX3D is free software: you can redistribute it and/or modify
15 it under the terms of the GNU Lesser Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 FreeWRL/FreeX3D is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with FreeWRL/FreeX3D. If not, see <http://www.gnu.org/licenses/>.
26****************************************************************************/
27
28
29#include <config.h>
30
31#if !defined(FRONTEND_DOES_SNAPSHOTS)
32
33#include <system.h>
34#include <display.h>
35#include <internal.h>
36
37#include <libFreeWRL.h>
38
39#include "../vrml_parser/Structs.h"
40#include "headers.h"
41#include "../vrml_parser/CParseGeneral.h"
42#include "../world_script/CScripts.h"
43#include "Snapshot.h"
44#include "../scenegraph/Collision.h"
45#include "../scenegraph/quaternion.h"
46#include "../scenegraph/Viewer.h"
47#include "../input/SensInterps.h"
48#include "../x3d_parser/Bindable.h"
49
50#if HAVE_DIRENT_H
51# include <dirent.h>
52#endif
53#ifdef HAVE_IMLIB2
54#include <Imlib2.h>
55#endif
56
57
58typedef struct pSnapshot{
59 /* snapshot stuff */
60 int snapRawCount;//=0;
61 int snapGoodCount;//=0;
62
63 int snapGif;// = FALSE; /* --gif save as an animated GIF, not mpg */
64 char *snapsnapB;// = NULL; /* --snapb -single snapshot files */
65 const char *default_seqtmp;// = "freewrl_tmp"; /* default value for seqtmp */
66 char *seqtmp;// = NULL; /* --seqtmp - directory for temp files */
67 int doSnapshot;// = FALSE; /* are we doing a snapshot? */
68 int doPrintshot;// = FALSE; /* are we taking a snapshot in order to print? */
69 int savedSnapshot;// = FALSE;
70 int modeTesting; //when generating test fixtures and playback with commandline -R,-F,-P - for linux just save .rgb don't convert image
71}* ppSnapshot;
72void *Snapshot_constructor()
73{
74 void *v = MALLOCV(sizeof(struct pSnapshot));
75 memset(v,0,sizeof(struct pSnapshot));
76 return v;
77}
78
79void Snapshot_init(struct tSnapshot* t)
80{
81 //public
82 t->doSnapshot = FALSE;
83 //private
84 t->prv = Snapshot_constructor();
85 {
86 ppSnapshot p = (ppSnapshot)t->prv;
87 /* snapshot stuff */
88 p->snapRawCount=0;
89 p->snapGoodCount=0;
90
91 p->snapGif = FALSE; /* --gif save as an animated GIF, not mpg */
92 p->snapsnapB = NULL; /* --snapb -single snapshot files */
93 p->default_seqtmp = "freewrl_tmp"; /* default value for seqtmp */
94 p->seqtmp = NULL; /* --seqtmp - directory for temp files */
95 p->doSnapshot = FALSE; /* are we doing a snapshot? */
96 p->doPrintshot = FALSE; /* are we taking a snapshot in order to print? */
97 p->savedSnapshot = FALSE;
98 p->modeTesting = FALSE;
99
100 }
101}
102
103
104void set_snapshotModeTesting(int value)
105{
106 ppSnapshot p = (ppSnapshot)gglobal()->Snapshot.prv;
107 p->modeTesting = value;
108}
109int isSnapshotModeTesting()
110{
111 struct tSnapshot* t = &gglobal()->Snapshot;
112 struct pSnapshot* p = (struct pSnapshot*)t->prv;
113 return p->modeTesting;
114}
115
116
117void fwl_set_SnapFile(const char* file)
118{
119 ppSnapshot p = (ppSnapshot)gglobal()->Snapshot.prv;
120
121 p->snapsnapB = STRDUP(file);
122 TRACE_MSG("snapsnapB set to %s\n", p->snapsnapB);
123 printf("%s\n",p->snapsnapB);
124}
125
126void fwl_set_SnapTmp(const char* file)
127{
128 {
129 ttglobal tg = gglobal();
130 tg->Snapshot.doSnapshot = FALSE;
131 {
132 ppSnapshot p = (ppSnapshot)tg->Snapshot.prv;
133 p->seqtmp = STRDUP(file);
134 TRACE_MSG("seqtmp set to %s\n", p->seqtmp);
135 }
136
137 }
138}
139
140
141#ifdef _MSC_VER
142static char * grabScreen(int bytesPerPixel, int x, int y, int width, int height)
143{
144 /* copies opengl window pixels into a buffer */
145 int pixelType;
146 char *buffer;
147 if(bytesPerPixel == 3) pixelType = GL_RGB;
148 if(bytesPerPixel == 4) pixelType = GL_RGBA;
149 buffer = MALLOC (GLvoid *, bytesPerPixel*width*height*sizeof(char));
150
151 /* grab the data */
152 FW_GL_PIXELSTOREI (GL_UNPACK_ALIGNMENT, 1);
153 FW_GL_PIXELSTOREI (GL_PACK_ALIGNMENT, 1);
154
155 FW_GL_READPIXELS (x,y,width,height,pixelType,GL_UNSIGNED_BYTE, buffer);
156 return buffer;
157}
158#endif //_MSC_VER
159
160#if defined( _MSC_VER) || defined (IPHONE) || defined(AQUA)
161/* stubbs for now */
162void setSnapshot() {}
163void fwl_toggleSnapshot(){}
164void fwl_init_SnapGif(){}
165void saveSnapSequence() {}
166#endif
167
168#ifdef IPHONE
169void Snapshot () {}
170#endif
171
172#ifndef IPHONE
173
174
175#define FDWORD unsigned long
176#define FLONG long
177#define FWORD unsigned short
178#define FBYTE unsigned char
179#define FBI_RGB 0L
180
181typedef struct {
182 FDWORD biSize;
183 FLONG biWidth;
184 FLONG biHeight;
185 FWORD biPlanes;
186 FWORD biBitCount;
187 FDWORD biCompression;
188 FDWORD biSizeImage;
189 FLONG biXPelsPerMeter;
190 FLONG biYPelsPerMeter;
191 FDWORD biClrUsed;
192 FDWORD biClrImportant;
194//#include <pshpack2.h> //puspack and poppack are all to fix the WORD bfType struct 4-byte alignment problem
195//I just took bfType out, then I don't need special packing on a 32bit system. Not sure about 64.
196typedef struct {
197 //FWORD bfType;
198 FDWORD bfSize;
199 FWORD bfReserved1;
200 FWORD bfReserved2;
201 FDWORD bfOffBits;
203
204typedef struct {
205 FBYTE rgbBlue;
206 FBYTE rgbGreen;
207 FBYTE rgbRed;
208 FBYTE rgbReserved;
209} FWRGBQUAD;
210
211typedef struct {
212 FWBITMAPINFOHEADER bmiHeader;
213 FWRGBQUAD bmiColors[1];
215
216
217//is this like htonl ?
218static void fromLong(unsigned long myword, char *buffer)
219{
220 buffer[0] = (unsigned char)((myword & 0x000000ff) >> 0);
221 buffer[1] = (unsigned char)((myword & 0x0000ff00) >> 8);
222 buffer[2] = (unsigned char)((myword & 0x00ff0000) >> 16);
223 buffer[3] = (unsigned char)((myword & 0xff000000) >> 24);
224}
225
226//is this like htons ?
227static void fromShort(unsigned short myword, char *buffer)
228{
229 buffer[0] = (myword & 0x00ff) >> 0;
230 buffer[1] = (myword & 0xff00) >> 8;
231}
232
233
234//#include "Vfw.h" //.avi headers
235void saveSnapshotBMP(char *pathname, char *buffer,int bytesPerPixel,int width, int height)
236{
237 //tested for bytesPerPixel == 3 and incoming alignment 1 only
238 //(outgoing/written is byte-aligned 4)
239 int rowlength, extra, alignedwidth, i;
240
242 FWORD bfType;
244 char filler[3] = {'\0','\0','\0'};
245 FILE *fout;
246
247 //fname = "freewrl_snapshot.bmp";
248 //if(p->snapsnapB) fname = p->snapsnapB;
249 fout = fopen(pathname,"w+b");
250
251 if(bytesPerPixel == 3) bi.biCompression = FBI_RGB;
252 bi.biHeight = height;
253 bi.biWidth = width;
254 bi.biPlanes = 1;
255 bi.biBitCount = 8 * bytesPerPixel;
256 bi.biXPelsPerMeter = 0;
257 bi.biYPelsPerMeter = 0;
258 //bi.biSizeImage = bytesPerPixel*bi.biHeight*bi.biWidth;
259 // The width must be DWORD (4byte) aligned unless the bitmap is RLE
260 // compressed.
261 rowlength = width*bytesPerPixel;
262 extra = 4 - (rowlength % 4);
263 if(extra == 4) extra = 0;
264 alignedwidth = rowlength + extra;
265 //bi.biSizeImage = ((bi.biWidth * 8*bytesPerPixel +31) & ~31) /8
266 // * bi.biHeight;
267 bi.biSizeImage = alignedwidth * height;
268 bi.biSize = sizeof(bi);
269 bi.biClrUsed = 0;
270 bi.biClrImportant = 0;
271 //printf("width=%d height=%d rowlengthmod4= %d extra=%d\n",width,height,rowlength%4,extra);
272 if(0){
273 //problem 1 - 64bit compiler may pad struct to 8 bytes
274 //problem 2 - we may be writing on a big-endian machine, .bmp numbers should be little endian
275 //memcpy(&bmph.bfType,"BM",2);
276 memcpy(&bfType,"BM",2);
277 bmph.bfReserved1 = 0;
278 bmph.bfReserved2 = 0;
279 bmph.bfOffBits = sizeof(bfType) + sizeof(FWBITMAPFILEHEADER) + sizeof(FWBITMAPINFOHEADER);
280 bmph.bfSize = sizeof(bfType) + sizeof(FWBITMAPFILEHEADER) + sizeof(FWBITMAPINFOHEADER) + bi.biSizeImage;
281 fwrite(&bfType,sizeof(bfType),1,fout);
282 fwrite(&bmph,sizeof(FWBITMAPFILEHEADER),1,fout);
283 fwrite(&bi,sizeof(FWBITMAPINFO),1,fout); //this is wrong. I'm writing 4 bytes for a color map I don't need to
284 if(true) //reverse colors
285 {
286 //swap GRB TO RGB //this is wrong because I wrote out 4 extra bytes above
287 int i;
288 char c;
289 for(i=0;i<rowlength*height;i+=3)
290 {
291 c = buffer[i];
292 buffer[i] = buffer[i+1];
293 buffer[i+1] = c;
294 }
295 }
296 }
297 if(1){
298 //solution 1: write each variable separately to buffer to elliminate struct padding
299 //solution 2: convert any-endian to little-endian with byte-reordering functions
300 char buf[128];
301 fwrite("BM",2,1,fout);
302 bi.biSize = 40; //9 longs x 4byte + 2 shorts x 2byte = 36 + 4 = 40
303 bmph.bfOffBits = 2 + 12 + bi.biSize; //2 + 12 + 40 = 54
304 bmph.bfSize = bmph.bfOffBits + bi.biSizeImage;
305 bmph.bfReserved1 = 0;
306 bmph.bfReserved2 = 0;
307
308 fromLong(bmph.bfSize, &buf[0]); //4
309 fromShort(bmph.bfReserved1, &buf[4]); //2
310 fromShort(bmph.bfReserved2, &buf[6]); //2
311 fromLong(bmph.bfOffBits, &buf[8]); //4
312 fromLong(bi.biSize, &buf[12]); //4
313
314 fromLong(bi.biWidth, &buf[16]);//4
315 fromLong(bi.biHeight, &buf[20]);//4
316 fromShort(bi.biPlanes, &buf[24]);//2
317 fromShort(bi.biBitCount, &buf[26]);//2
318 fromLong(bi.biCompression, &buf[28]);//4
319 fromLong(bi.biSizeImage, &buf[32]);//4
320 fromLong(bi.biXPelsPerMeter, &buf[36]);//4
321 fromLong(bi.biYPelsPerMeter, &buf[40]);//4
322 fromLong(bi.biClrUsed, &buf[44]);//4
323 fromLong(bi.biClrImportant, &buf[48]);//4
324 fwrite(buf,52,1,fout);
325 if(true) //reverse order
326 {
327 //swap BGR TO RGB
328 int i;
329 char c;
330 for(i=0;i<rowlength*height;i+=3)
331 {
332 c = buffer[i];
333 buffer[i] = buffer[i+2];
334 buffer[i+2] = c;
335 }
336 }
337 }
338 //write by row - and do byte alignment 4 (assume incoming is byte aligned 1)
339 for(i=0;i<height;i++)
340 {
341 int j=i*rowlength;
342 fwrite(&buffer[j],rowlength,1,fout);
343 if(extra)
344 fwrite(filler,extra,1,fout);
345 }
346 fclose(fout);
347}
348#endif
349
350#ifdef _MSC_VER
351int fw_mkdir(const char* path);
352void Snapshot ()
353{
354/* going to try just the single snapshot for windows, to .bmp format
355 (and future: remember .avi holds a sequence of DIBs. A .bmp holds 1 DIB.
356 There is something in the 2003 platform SDK and online for AVI & RIFF/DIB/BMP
357 http://msdn.microsoft.com/en-us/library/dd145119(v=VS.85).aspx storing a bitmap
358 http://msdn.microsoft.com/en-us/library/dd183391(VS.85).aspx .bmp
359 http://msdn.microsoft.com/en-us/library/aa446563.aspx sample program
360 http://msdn.microsoft.com/en-us/library/ms706540(v=VS.85).aspx
361 http://msdn.microsoft.com/en-us/library/ms706415(v=VS.85).aspx Vfw.h, Vfw32.lib
362*/
363 char thisRawFile[2000];
364 char *mysnapb, *mytmp;
365 char *imgbuf;
366 ppSnapshot p = (ppSnapshot)gglobal()->Snapshot.prv;
367
368 imgbuf = grabScreen(3,0,0,gglobal()->display.screenWidth,gglobal()->display.screenHeight);
369 if (p->snapsnapB == NULL)
370 mysnapb = "freewrl.snap";
371 else
372 mysnapb = p->snapsnapB;
373
374 if (p->seqtmp == NULL) mytmp = "freewrl_tmp";
375 else mytmp = p->seqtmp;
376
377 fw_mkdir(mytmp);
378 p->snapRawCount ++;
379 snprintf(thisRawFile, sizeof(thisRawFile), "%s/%s.%04d.bmp", mytmp, mysnapb, p->snapRawCount);
380 saveSnapshotBMP(thisRawFile, imgbuf, 3, gglobal()->display.screenWidth, gglobal()->display.screenHeight);
381 FREE(imgbuf);
382}
383void Snapshot1(char *fname){
384 char *imgbuf;
385 imgbuf = grabScreen(3,0,0,gglobal()->display.screenWidth,gglobal()->display.screenHeight);
386 saveSnapshotBMP(fname, imgbuf, 3, gglobal()->display.screenWidth, gglobal()->display.screenHeight);
387 FREE(imgbuf);
388}
389#endif /*ifdef win32*/
390#if !(defined(_MSC_VER) || defined(IPHONE) || defined(AQUA))
391
392void fwl_init_SnapGif()
393{
394 //struct pSnapshot* p = (struct pSnapshot*)gglobal()->Snapshot.prv;
395 ppSnapshot p = (ppSnapshot)gglobal()->Snapshot.prv;
396 p->snapGif = TRUE;
397}
398
399
400void saveSnapshotBmp0(char *folder, char *prefix, const char *sufx, int count, void *buffer, int bpp, int width, int height){
401 char thisRawFile[2000];
402 snprintf (thisRawFile, sizeof(thisRawFile),"%s/%s.%04d.bmp",folder,prefix,count);
403 saveSnapshotBMP(thisRawFile,buffer,3,gglobal()->display.screenWidth, gglobal()->display.screenHeight);
404 printf ("[2] snapshot is: %s\n",thisRawFile);
405}
406#ifdef HAVE_IMLIB2
407void saveSnapshotImlib2Png(char *folder, char *prefix, const char *sufx, int count, char *buffer, int bpp, int width, int height){
408 char thisRawFile[2000];
409 int i,ii,j,k,kk;
410 char* buf32;
411 char *inrow, *outrow;
412
413 snprintf (thisRawFile, sizeof(thisRawFile),"%s/%s.%04d.%s",folder,prefix,count,sufx);
414 // https://docs.enlightenment.org/api/imlib2/html/
415 Imlib_Image image;
416 image = imlib_create_image(width,height);
417 imlib_context_set_image(image);
418 imlib_image_set_has_alpha(0);
419 imlib_image_set_format(sufx);
420 buf32 = (char *)imlib_image_get_data();
421 for(i=0;i<height;i++){
422 inrow = &buffer[(width * bpp)*i];
423 outrow = &buf32[(width *4)*(height -i-1)]; //flip
424 for(j=0;j<width;j++){
425 outrow[j*4 +3] = 255; //how to set transparency?
426 for(k=0;k<bpp;k++){
427 kk = 2 - k; //flip R and B
428 outrow[j*4 +kk] = inrow[j*bpp +k];
429 }
430 }
431 }
432 imlib_image_put_back_data((DATA32*)buf32);
433 imlib_save_image(thisRawFile);
434 imlib_free_image();
435 printf ("[2] snapshot is: %s\n",thisRawFile);
436}
437#endif //HAVE_IMLIB2
438void saveSnapshotRawPng(char *folder, char *prefix, const char *sufx, int count, void *buffer, int bpp, int width, int height){
439 char thisRawFile[2000];
440 char thisGoodFile[2000];
441 char sysline[2000];
442
443 FILE * tmpfile;
444
445 snprintf (thisRawFile, sizeof(thisRawFile),"%s/%s.%04d.rgb",folder,prefix,count);
446 tmpfile = fopen(thisRawFile,"w");
447 if (tmpfile == NULL) {
448 printf ("cannot open temp file (%s) for writing\n",thisRawFile);
449 FREE_IF_NZ (buffer);
450 return;
451 }
452
453 if (fwrite(buffer, 1, height*width*3, tmpfile) <= 0) {
454 printf ("error writing snapshot to %s, aborting snapshot\n",thisRawFile);
455 FREE_IF_NZ (buffer);
456 return;
457 }
458 fclose (tmpfile);
459
460 /* convert -size 450x300 -depth 8 -flip /tmp/snappedfile.rgb out.png works. */
461
462
463 snprintf (thisGoodFile, sizeof(thisGoodFile),"%s/%s.%04d.%s",folder,prefix,count,sufx);
464 snprintf(sysline,sizeof(sysline),"%s -size %dx%d -depth 8 -flip %s %s",
465 IMAGECONVERT,width, height,thisRawFile,thisGoodFile);
466
467 if (system (sysline) != 0) {
468 printf ("Freewrl: error running convert line %s\n",sysline);
469 }
470 printf ("[2] snapshot is: %s\n",thisGoodFile);
471 UNLINK (thisRawFile);
472
473}
474
475/* get 1 frame; convert if we are doing 1 image at a time */
476static const char * suffix [] = {"png","gif"};
477void Snapshot () {
478 GLvoid *buffer;
479 DIR *mydir;
480 const char *sufx;
481
482 char *mytmp, *mysnapb;
483
484 struct tSnapshot* t = &gglobal()->Snapshot;
485 struct pSnapshot* p = (struct pSnapshot*)t->prv;
486
487
488 printf("do Snapshot ... \n");
489 /* make up base names - these may be command line parameters */
490
491 if (p->snapsnapB == NULL)
492 mysnapb = "freewrl.snap";
493 else
494 mysnapb = p->snapsnapB;
495
496
497 if (p->seqtmp == NULL) mytmp = "freewrl_tmp";
498 else mytmp = p->seqtmp;
499
500 /*does the directory exist? */
501 if ((mydir = opendir(mytmp)) == NULL) {
502 mkdir (mytmp,0755);
503 if ((mydir = opendir(mytmp)) == NULL) {
504 ConsoleMessage ("error opening Snapshot directory %s\n",mytmp);
505 return;
506 }
507 }
508
509
510 /* Linux, etc, can get by with 3 bytes per pixel */
511 /* MALLOC 3 bytes per pixel */
512 buffer = MALLOC (GLvoid *, 3*gglobal()->display.screenWidth*gglobal()->display.screenHeight*sizeof(char));
513
514 /* grab the data */
515 FW_GL_PIXELSTOREI (GL_UNPACK_ALIGNMENT, 1);
516 FW_GL_PIXELSTOREI (GL_PACK_ALIGNMENT, 1);
517 FW_GL_READPIXELS (0,0,gglobal()->display.screenWidth,gglobal()->display.screenHeight,GL_RGB,GL_UNSIGNED_BYTE, buffer);
518
519
520 /* save this snapshot */
521 p->snapRawCount ++;
522
523 sufx = suffix[0];
524 if(p->snapGif) sufx = suffix[1];
525 /* save the file */
526 if(p->modeTesting){
527 saveSnapshotBmp0(mytmp,mysnapb,sufx,p->snapRawCount,buffer,3,gglobal()->display.screenWidth, gglobal()->display.screenHeight);
528 }else{
529#ifdef HAVE_IMLIB2
530 saveSnapshotImlib2Png(mytmp,mysnapb,sufx,p->snapRawCount,buffer,3,gglobal()->display.screenWidth, gglobal()->display.screenHeight);
531#else
532 saveSnapshotRawPng(mytmp,mysnapb,sufx,p->snapRawCount,buffer,3,gglobal()->display.screenWidth, gglobal()->display.screenHeight);
533#endif
534 }
535 FREE_IF_NZ (buffer);
536}
537#endif /*ifdef win32*/
538
539#endif