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 memset(&bi, 0, sizeof(FWBITMAPINFOHEADER));
247
248 //fname = "freewrl_snapshot.bmp";
249 //if(p->snapsnapB) fname = p->snapsnapB;
250 fout = fopen(pathname,"w+b");
251
252 if(bytesPerPixel == 3) bi.biCompression = FBI_RGB;
253 bi.biHeight = height;
254 bi.biWidth = width;
255 bi.biPlanes = 1;
256 bi.biBitCount = 8 * bytesPerPixel;
257 bi.biXPelsPerMeter = 0;
258 bi.biYPelsPerMeter = 0;
259 //bi.biSizeImage = bytesPerPixel*bi.biHeight*bi.biWidth;
260 // The width must be DWORD (4byte) aligned unless the bitmap is RLE
261 // compressed.
262 rowlength = width*bytesPerPixel;
263 extra = 4 - (rowlength % 4);
264 if(extra == 4) extra = 0;
265 alignedwidth = rowlength + extra;
266 //bi.biSizeImage = ((bi.biWidth * 8*bytesPerPixel +31) & ~31) /8
267 // * bi.biHeight;
268 bi.biSizeImage = alignedwidth * height;
269 bi.biSize = sizeof(bi);
270 bi.biClrUsed = 0;
271 bi.biClrImportant = 0;
272 //printf("width=%d height=%d rowlengthmod4= %d extra=%d\n",width,height,rowlength%4,extra);
273 if(0){
274 //problem 1 - 64bit compiler may pad struct to 8 bytes
275 //problem 2 - we may be writing on a big-endian machine, .bmp numbers should be little endian
276 //memcpy(&bmph.bfType,"BM",2);
277 memcpy(&bfType,"BM",2);
278 bmph.bfReserved1 = 0;
279 bmph.bfReserved2 = 0;
280 bmph.bfOffBits = sizeof(bfType) + sizeof(FWBITMAPFILEHEADER) + sizeof(FWBITMAPINFOHEADER);
281 bmph.bfSize = sizeof(bfType) + sizeof(FWBITMAPFILEHEADER) + sizeof(FWBITMAPINFOHEADER) + bi.biSizeImage;
282 fwrite(&bfType,sizeof(bfType),1,fout);
283 fwrite(&bmph,sizeof(FWBITMAPFILEHEADER),1,fout);
284 fwrite(&bi,sizeof(FWBITMAPINFO),1,fout); //this is wrong. I'm writing 4 bytes for a color map I don't need to
285 if(true) //reverse colors
286 {
287 //swap GRB TO RGB //this is wrong because I wrote out 4 extra bytes above
288 int i;
289 char c;
290 for(i=0;i<rowlength*height;i+=3)
291 {
292 c = buffer[i];
293 buffer[i] = buffer[i+1];
294 buffer[i+1] = c;
295 }
296 }
297 }
298 if(1){
299 //solution 1: write each variable separately to buffer to elliminate struct padding
300 //solution 2: convert any-endian to little-endian with byte-reordering functions
301 char buf[128];
302 fwrite("BM",2,1,fout);
303 bi.biSize = 40; //9 longs x 4byte + 2 shorts x 2byte = 36 + 4 = 40
304 bmph.bfOffBits = 2 + 12 + bi.biSize; //2 + 12 + 40 = 54
305 bmph.bfSize = bmph.bfOffBits + bi.biSizeImage;
306 bmph.bfReserved1 = 0;
307 bmph.bfReserved2 = 0;
308
309 fromLong(bmph.bfSize, &buf[0]); //4
310 fromShort(bmph.bfReserved1, &buf[4]); //2
311 fromShort(bmph.bfReserved2, &buf[6]); //2
312 fromLong(bmph.bfOffBits, &buf[8]); //4
313 fromLong(bi.biSize, &buf[12]); //4
314
315 fromLong(bi.biWidth, &buf[16]);//4
316 fromLong(bi.biHeight, &buf[20]);//4
317 fromShort(bi.biPlanes, &buf[24]);//2
318 fromShort(bi.biBitCount, &buf[26]);//2
319 fromLong(bi.biCompression, &buf[28]);//4
320 fromLong(bi.biSizeImage, &buf[32]);//4
321 fromLong(bi.biXPelsPerMeter, &buf[36]);//4
322 fromLong(bi.biYPelsPerMeter, &buf[40]);//4
323 fromLong(bi.biClrUsed, &buf[44]);//4
324 fromLong(bi.biClrImportant, &buf[48]);//4
325 fwrite(buf,52,1,fout);
326 if(true) //reverse order
327 {
328 //swap BGR TO RGB
329 int i;
330 char c;
331 for(i=0;i<rowlength*height;i+=bytesPerPixel)
332 {
333 c = buffer[i];
334 buffer[i] = buffer[i+2];
335 buffer[i+2] = c;
336 }
337 }
338 }
339 //write by row - and do byte alignment 4 (assume incoming is byte aligned 1)
340 for(i=0;i<height;i++)
341 {
342 int j=i*rowlength;
343 fwrite(&buffer[j],rowlength,1,fout);
344 if(extra)
345 fwrite(filler,extra,1,fout);
346 }
347 fclose(fout);
348}
349#endif
350
351#ifdef _MSC_VER
352int fw_mkdir(const char* path);
353void Snapshot ()
354{
355/* going to try just the single snapshot for windows, to .bmp format
356 (and future: remember .avi holds a sequence of DIBs. A .bmp holds 1 DIB.
357 There is something in the 2003 platform SDK and online for AVI & RIFF/DIB/BMP
358 http://msdn.microsoft.com/en-us/library/dd145119(v=VS.85).aspx storing a bitmap
359 http://msdn.microsoft.com/en-us/library/dd183391(VS.85).aspx .bmp
360 http://msdn.microsoft.com/en-us/library/aa446563.aspx sample program
361 http://msdn.microsoft.com/en-us/library/ms706540(v=VS.85).aspx
362 http://msdn.microsoft.com/en-us/library/ms706415(v=VS.85).aspx Vfw.h, Vfw32.lib
363*/
364 char thisRawFile[2000];
365 char *mysnapb, *mytmp;
366 char *imgbuf;
367 ppSnapshot p = (ppSnapshot)gglobal()->Snapshot.prv;
368
369 imgbuf = grabScreen(3,0,0,gglobal()->display.screenWidth,gglobal()->display.screenHeight);
370 if (p->snapsnapB == NULL)
371 mysnapb = "freewrl.snap";
372 else
373 mysnapb = p->snapsnapB;
374
375 if (p->seqtmp == NULL) mytmp = "freewrl_tmp";
376 else mytmp = p->seqtmp;
377
378 fw_mkdir(mytmp);
379 p->snapRawCount ++;
380 snprintf(thisRawFile, sizeof(thisRawFile), "%s/%s.%04d.bmp", mytmp, mysnapb, p->snapRawCount);
381 saveSnapshotBMP(thisRawFile, imgbuf, 3, gglobal()->display.screenWidth, gglobal()->display.screenHeight);
382 FREE(imgbuf);
383}
384void Snapshot1(char *fname){
385 char *imgbuf;
386 imgbuf = grabScreen(3,0,0,gglobal()->display.screenWidth,gglobal()->display.screenHeight);
387 saveSnapshotBMP(fname, imgbuf, 3, gglobal()->display.screenWidth, gglobal()->display.screenHeight);
388 FREE(imgbuf);
389}
390#endif /*ifdef win32*/
391#if !(defined(_MSC_VER) || defined(IPHONE) || defined(AQUA))
392
393void fwl_init_SnapGif()
394{
395 //struct pSnapshot* p = (struct pSnapshot*)gglobal()->Snapshot.prv;
396 ppSnapshot p = (ppSnapshot)gglobal()->Snapshot.prv;
397 p->snapGif = TRUE;
398}
399
400
401void saveSnapshotBmp0(char *folder, char *prefix, const char *sufx, int count, void *buffer, int bpp, int width, int height){
402 char thisRawFile[2000];
403 snprintf (thisRawFile, sizeof(thisRawFile),"%s/%s.%04d.bmp",folder,prefix,count);
404 saveSnapshotBMP(thisRawFile,buffer,3,gglobal()->display.screenWidth, gglobal()->display.screenHeight);
405 printf ("[2] snapshot is: %s\n",thisRawFile);
406}
407#ifdef HAVE_IMLIB2
408void saveSnapshotImlib2Png(char *folder, char *prefix, const char *sufx, int count, char *buffer, int bpp, int width, int height){
409 char thisRawFile[2000];
410 int i,ii,j,k,kk;
411 char* buf32;
412 char *inrow, *outrow;
413
414 snprintf (thisRawFile, sizeof(thisRawFile),"%s/%s.%04d.%s",folder,prefix,count,sufx);
415 // https://docs.enlightenment.org/api/imlib2/html/
416 Imlib_Image image;
417 image = imlib_create_image(width,height);
418 imlib_context_set_image(image);
419 imlib_image_set_has_alpha(0);
420 imlib_image_set_format(sufx);
421 buf32 = (char *)imlib_image_get_data();
422 for(i=0;i<height;i++){
423 inrow = &buffer[(width * bpp)*i];
424 outrow = &buf32[(width *4)*(height -i-1)]; //flip
425 for(j=0;j<width;j++){
426 outrow[j*4 +3] = 255; //how to set transparency?
427 for(k=0;k<bpp;k++){
428 kk = 2 - k; //flip R and B
429 outrow[j*4 +kk] = inrow[j*bpp +k];
430 }
431 }
432 }
433 imlib_image_put_back_data((DATA32*)buf32);
434 imlib_save_image(thisRawFile);
435 imlib_free_image();
436 printf ("[2] snapshot is: %s\n",thisRawFile);
437}
438#endif //HAVE_IMLIB2
439void saveSnapshotRawPng(char *folder, char *prefix, const char *sufx, int count, void *buffer, int bpp, int width, int height){
440 char thisRawFile[2000];
441 char thisGoodFile[2000];
442 char sysline[2000];
443
444 FILE * tmpfile;
445
446 snprintf (thisRawFile, sizeof(thisRawFile),"%s/%s.%04d.rgb",folder,prefix,count);
447 tmpfile = fopen(thisRawFile,"w");
448 if (tmpfile == NULL) {
449 printf ("cannot open temp file (%s) for writing\n",thisRawFile);
450 FREE_IF_NZ (buffer);
451 return;
452 }
453
454 if (fwrite(buffer, 1, height*width*3, tmpfile) <= 0) {
455 printf ("error writing snapshot to %s, aborting snapshot\n",thisRawFile);
456 FREE_IF_NZ (buffer);
457 return;
458 }
459 fclose (tmpfile);
460
461 /* convert -size 450x300 -depth 8 -flip /tmp/snappedfile.rgb out.png works. */
462
463
464 snprintf (thisGoodFile, sizeof(thisGoodFile),"%s/%s.%04d.%s",folder,prefix,count,sufx);
465 snprintf(sysline,sizeof(sysline),"%s -size %dx%d -depth 8 -flip %s %s",
466 IMAGECONVERT,width, height,thisRawFile,thisGoodFile);
467
468 if (system (sysline) != 0) {
469 printf ("Freewrl: error running convert line %s\n",sysline);
470 }
471 printf ("[2] snapshot is: %s\n",thisGoodFile);
472 UNLINK (thisRawFile);
473
474}
475
476/* get 1 frame; convert if we are doing 1 image at a time */
477static const char * suffix [] = {"png","gif"};
478void Snapshot () {
479 GLvoid *buffer;
480 DIR *mydir;
481 const char *sufx;
482
483 char *mytmp, *mysnapb;
484
485 struct tSnapshot* t = &gglobal()->Snapshot;
486 struct pSnapshot* p = (struct pSnapshot*)t->prv;
487
488
489 printf("do Snapshot ... \n");
490 /* make up base names - these may be command line parameters */
491
492 if (p->snapsnapB == NULL)
493 mysnapb = "freewrl.snap";
494 else
495 mysnapb = p->snapsnapB;
496
497
498 if (p->seqtmp == NULL) mytmp = "freewrl_tmp";
499 else mytmp = p->seqtmp;
500
501 /*does the directory exist? */
502 if ((mydir = opendir(mytmp)) == NULL) {
503 mkdir (mytmp,0755);
504 if ((mydir = opendir(mytmp)) == NULL) {
505 ConsoleMessage ("error opening Snapshot directory %s\n",mytmp);
506 return;
507 }
508 }
509
510
511 /* Linux, etc, can get by with 3 bytes per pixel */
512 /* MALLOC 3 bytes per pixel */
513 buffer = MALLOC (GLvoid *, 3*gglobal()->display.screenWidth*gglobal()->display.screenHeight*sizeof(char));
514
515 /* grab the data */
516 FW_GL_PIXELSTOREI (GL_UNPACK_ALIGNMENT, 1);
517 FW_GL_PIXELSTOREI (GL_PACK_ALIGNMENT, 1);
518 FW_GL_READPIXELS (0,0,gglobal()->display.screenWidth,gglobal()->display.screenHeight,GL_RGB,GL_UNSIGNED_BYTE, buffer);
519
520
521 /* save this snapshot */
522 p->snapRawCount ++;
523
524 sufx = suffix[0];
525 if(p->snapGif) sufx = suffix[1];
526 /* save the file */
527 if(p->modeTesting){
528 saveSnapshotBmp0(mytmp,mysnapb,sufx,p->snapRawCount,buffer,3,gglobal()->display.screenWidth, gglobal()->display.screenHeight);
529 }else{
530#ifdef HAVE_IMLIB2
531 saveSnapshotImlib2Png(mytmp,mysnapb,sufx,p->snapRawCount,buffer,3,gglobal()->display.screenWidth, gglobal()->display.screenHeight);
532#else
533 saveSnapshotRawPng(mytmp,mysnapb,sufx,p->snapRawCount,buffer,3,gglobal()->display.screenWidth, gglobal()->display.screenHeight);
534#endif
535 }
536 FREE_IF_NZ (buffer);
537}
538#endif /*ifdef win32*/
539
540#endif