2
// Copyright � 1997 - 2001, Paul C. Gregory
4
// Contact: pgregory@aqsis.com
6
// This library is free software; you can redistribute it and/or
7
// modify it under the terms of the GNU General Public
8
// License as published by the Free Software Foundation; either
9
// version 2 of the License, or (at your option) any later version.
11
// This library is distributed in the hope that it will be useful,
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
// General Public License for more details.
16
// You should have received a copy of the GNU General Public
17
// License along with this library; if not, write to the Free Software
18
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
\brief Implements a GLUT based framebuffer display driver for Aqsis
23
\author Timothy M. Shead (tshead@k-3d.com)
34
#include <logging_streambufs.h>
36
#ifdef AQSIS_SYSTEM_WIN32
42
#define getcwd _getcwd
43
#define PATH_SEPARATOR "\\"
45
#else // !AQSIS_SYSTEM_WIN32
51
#include <netinet/in.h>
52
#include <sys/types.h>
53
#include <sys/socket.h>
56
typedef sockaddr_in SOCKADDR_IN;
57
typedef sockaddr* PSOCKADDR;
59
static const int INVALID_SOCKET = -1;
60
static const int SOCKET_ERROR = -1;
62
#define PATH_SEPARATOR "/"
64
#endif // !AQSIS_SYSTEM_WIN32
68
#include "displaydriver.h"
71
using namespace Aqsis;
73
#ifdef AQSIS_SYSTEM_MACOSX
74
#include <GLUT/glut.h>
75
#include <GLUT/macxglut_utilities.h>
76
#include <ApplicationServices/ApplicationServices.h>
79
#endif //!AQSIS_SYSTEM_MACOSX
81
#ifndef AQSIS_SYSTEM_WIN32
83
#endif // !AQSIS_SYSTEM_WIN32
85
static std::string g_Filename( "output.tif" );
86
static TqInt g_ImageWidth = 0;
87
static TqInt g_ImageHeight = 0;
88
static TqInt g_PixelsProcessed = 0;
89
static TqInt g_Channels = 0;
90
static TqInt g_Format = 0;
91
static int g_Window = 0;
92
static GLubyte* g_Image = 0;
93
static TqInt g_CWXmin, g_CWYmin;
94
static TqInt g_CWXmax, g_CWYmax;
95
static TqFloat quantize_zeroval = 0.0f;
96
static TqFloat quantize_oneval = 0.0f;
97
static TqFloat quantize_minval = 0.0f;
98
static TqFloat quantize_maxval = 0.0f;
99
static TqFloat dither_val = 0.0f;
101
typedef void (text_callback)(const std::string&);
102
static std::string g_text_prompt;
103
static std::string g_text_input;
104
static text_callback* g_text_ok_callback = 0;
106
const std::string get_window_title()
108
std::ostringstream buffer;
109
buffer << g_Filename << ": " << std::fixed << std::setprecision(1) << 100.0 * static_cast<double>(g_PixelsProcessed) / static_cast<double>(g_ImageWidth * g_ImageHeight) << "% complete";
114
const std::string get_current_working_directory()
116
std::string result(1024, '\0');
117
getcwd(const_cast<char*>(result.c_str()), result.size());
118
result.resize(strlen(result.c_str()));
123
const std::string append_path(const std::string& LHS, const std::string& RHS)
125
return LHS + PATH_SEPARATOR + RHS;
128
void write_tiff(const std::string& filename)
130
TIFF* const file = TIFFOpen(filename.c_str(), "w");
133
std::cerr << error << "Could not open [" << filename << "] for TIFF output" << std::endl;
137
TIFFSetField(file, TIFFTAG_IMAGEWIDTH, g_ImageWidth);
138
TIFFSetField(file, TIFFTAG_IMAGELENGTH, g_ImageHeight);
139
TIFFSetField(file, TIFFTAG_BITSPERSAMPLE, 8);
140
TIFFSetField(file, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
141
TIFFSetField(file, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
142
TIFFSetField(file, TIFFTAG_SAMPLESPERPIXEL, 3);
143
TIFFSetField(file, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
144
TIFFSetField(file, TIFFTAG_ROWSPERSTRIP, 1);
145
TIFFSetField(file, TIFFTAG_IMAGEDESCRIPTION, "Image rendered with Aqsis, http://www.aqsis.com");
147
GLubyte* p = g_Image;
148
for(int i = g_ImageHeight - 1; i >= 0; i--)
150
if(TIFFWriteScanline(file, p, i, 0) < 0)
153
std::cerr << error << "Could not write data to [" << filename << "] for TIFF output" << std::endl;
156
p += g_ImageWidth * sizeof(GLubyte) * 3;
162
void text_prompt(const std::string& Prompt, const std::string& DefaultValue, text_callback* Callback)
164
g_text_prompt = Prompt;
165
g_text_input = DefaultValue;
166
g_text_ok_callback = Callback;
176
text_prompt("Save TIFF: ", append_path(get_current_working_directory(), g_Filename), &write_tiff);
181
void draw_text(const std::string& Text)
183
for(std::string::const_iterator c = Text.begin(); c != Text.end(); ++c)
184
glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, *c);
190
glRasterPos2i( 0, 0 );
191
glDrawPixels( g_ImageWidth, g_ImageHeight, GL_RGB, GL_UNSIGNED_BYTE, g_Image );
193
// Prompt the user for input ...
194
if(g_text_prompt.size())
197
glGetDoublev(GL_VIEWPORT, viewport);
200
glColor4d(0, 0, 0.5, 0.5);
201
glRectd(viewport[0], viewport[3] / 2 + 20, viewport[2], viewport[3] / 2 - 10);
203
glColor4d(1, 1, 1, 1);
204
glRasterPos2d(viewport[0] + 10, viewport[3] / 2);
205
draw_text(g_text_prompt);
206
draw_text(g_text_input);
216
glDisable( GL_SCISSOR_TEST );
217
glClear( GL_COLOR_BUFFER_BIT );
222
void reshape( int w, int h )
224
glViewport( 0, 0, ( GLsizei ) w, ( GLsizei ) h );
225
glMatrixMode( GL_PROJECTION );
227
gluOrtho2D( 0.0, ( GLdouble ) w, 0.0, ( GLdouble ) h );
228
glMatrixMode( GL_MODELVIEW );
234
if ( !DDProcessMessageAsync( 0, 1000 ) )
238
void keyboard( unsigned char key, int x, int y )
240
// If the text prompt is active, it consumes all input ...
241
if(g_text_prompt.size())
246
if(g_text_input.size())
248
std::string::iterator i = g_text_input.end();
249
g_text_input.erase(--i);
257
g_text_ok_callback = 0;
262
g_text_ok_callback(g_text_input);
265
g_text_ok_callback = 0;
278
// No text prompt ...
282
text_prompt("Save TIFF: ", append_path(get_current_working_directory(), g_Filename), &write_tiff);
293
int main( int argc, char** argv )
295
std::auto_ptr<std::streambuf> reset_level( new Aqsis::reset_level_buf(std::cerr) );
296
std::auto_ptr<std::streambuf> show_timestamps( new Aqsis::timestamp_buf(std::cerr) );
297
std::auto_ptr<std::streambuf> fold_duplicates( new Aqsis::fold_duplicates_buf(std::cerr) );
298
std::auto_ptr<std::streambuf> show_level( new Aqsis::show_level_buf(std::cerr) );
299
std::auto_ptr<std::streambuf> filter_level( new Aqsis::filter_by_level_buf(Aqsis::WARNING, std::cerr) );
302
char *portStr = getenv( "AQSIS_DD_PORT" );
304
if ( portStr != NULL )
306
port = atoi( portStr );
309
if ( -1 == DDInitialise( NULL, port ) )
311
std::cerr << error << "Could not open communications channel to Aqsis" << std::endl;
315
glutInit( &argc, argv );
317
// Process messages until we have enough data to create our window ...
318
while ( 0 == g_Window )
320
if ( !DDProcessMessage() )
322
std::cerr << "Premature end of messages" << std::endl;
327
// Start the glut message loop ...
328
glutDisplayFunc( full_display );
329
glutReshapeFunc( reshape );
330
glutKeyboardFunc( keyboard );
331
glutIdleFunc( idle );
332
glutCreateMenu(menu);
333
glutAddMenuEntry("Write TIFF file", 1);
334
glutAttachMenu(GLUT_RIGHT_BUTTON);
337
glClearColor( 0.0, 0.0, 0.0, 0.0 );
338
glDisable( GL_DEPTH_TEST );
339
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
340
glShadeModel( GL_FLAT );
341
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
342
glClear( GL_COLOR_BUFFER_BIT );
347
// Lose our image buffer ...
353
//----------------------------------------------------------------------------
354
// Functions required by libdd.
356
SqDDMessageFormatResponse frmt( DataFormat_Signed32 );
357
SqDDMessageCloseAcknowledge closeack;
359
TqInt Query( SOCKET s, SqDDMessageBase* pMsgB )
361
switch ( pMsgB->m_MessageID )
363
case MessageID_FormatQuery:
365
SqDDMessageFormatQuery* pMsg = static_cast<SqDDMessageFormatQuery*>(pMsgB);
366
g_Format = pMsg->m_Formats[0];
367
frmt.m_DataFormat = g_Format;
368
if ( DDSendMsg( s, &frmt ) <= 0 )
377
TqInt Open( SOCKET s, SqDDMessageBase* pMsgB )
379
SqDDMessageOpen * const message = static_cast<SqDDMessageOpen*>( pMsgB );
381
g_ImageWidth = ( message->m_CropWindowXMax - message->m_CropWindowXMin );
382
g_ImageHeight = ( message->m_CropWindowYMax - message->m_CropWindowYMin );
383
g_PixelsProcessed = 0;
385
g_Channels = message->m_Channels;
387
g_CWXmin = message->m_CropWindowXMin;
388
g_CWYmin = message->m_CropWindowYMin;
389
g_CWXmax = message->m_CropWindowXMax;
390
g_CWYmax = message->m_CropWindowYMax;
392
g_Image = new GLubyte[ g_ImageWidth * g_ImageHeight * 3 ];
393
//memset( g_Image, 128, g_ImageWidth * g_ImageHeight * 3 );
394
for (TqInt i = 0; i < g_ImageHeight; i ++) {
395
for (TqInt j=0; j < g_ImageWidth; j++)
400
if ( ( (g_ImageHeight - 1 - i) & 31 ) < 16 ) t ^= 1;
401
if ( ( j & 31 ) < 16 ) t ^= 1;
407
g_Image[3 * (i*g_ImageWidth + j) ] = d;
408
g_Image[3 * (i*g_ImageWidth + j) + 1] = d;
409
g_Image[3 * (i*g_ImageWidth + j) + 2] = d;
414
// glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
415
glutInitDisplayMode( GLUT_SINGLE | GLUT_RGBA );
416
glutInitWindowSize( g_ImageWidth, g_ImageHeight );
417
g_Window = glutCreateWindow( get_window_title().c_str() );
422
#define INT_MULT(a,b,t) ( (t) = (a) * (b) + 0x80, ( ( ( (t)>>8 ) + (t) )>>8 ) )
423
#define INT_PRELERP(p, q, a, t) ( (p) + (q) - INT_MULT( a, p, t) )
425
TqInt Data( SOCKET s, SqDDMessageBase* pMsgB )
427
SqDDMessageData * const message = static_cast<SqDDMessageData*>( pMsgB );
429
const TqInt linelength = g_ImageWidth * 3;
430
char* bucket = reinterpret_cast<char*>( &message->m_Data );
432
// CHeck if the beck is not at all within the crop window.
433
if ( message->m_XMin > g_CWXmax || message->m_XMaxPlus1 < g_CWXmin ||
434
message->m_YMin > g_CWYmax || message->m_YMaxPlus1 < g_CWYmin )
437
for ( TqInt y = message->m_YMin - g_CWYmin; y < message->m_YMaxPlus1 - g_CWYmin; y++ )
439
for ( TqInt x = message->m_XMin - g_CWXmin; x < message->m_XMaxPlus1 - g_CWXmin; x++ )
441
if ( x >= 0 && y >= 0 && x < g_ImageWidth && y < g_ImageHeight )
443
const TqInt so = ( ( g_ImageHeight - y - 1 ) * linelength ) + ( x * 3 );
445
TqFloat value0, value1, value2;
446
TqFloat alpha = 255.0f;
447
if ( g_Channels >= 3 )
449
value0 = reinterpret_cast<TqFloat*>( bucket ) [ 0 ];
450
value1 = reinterpret_cast<TqFloat*>( bucket ) [ 1 ];
451
value2 = reinterpret_cast<TqFloat*>( bucket ) [ 2 ];
455
value0 = reinterpret_cast<TqFloat*>( bucket ) [ 0 ];
456
value1 = reinterpret_cast<TqFloat*>( bucket ) [ 0 ];
457
value2 = reinterpret_cast<TqFloat*>( bucket ) [ 0 ];
460
if ( g_Channels > 3 )
461
alpha = (reinterpret_cast<TqFloat*>( bucket ) [ 3 ]);
463
if( !( quantize_zeroval == 0.0f &&
464
quantize_oneval == 0.0f &&
465
quantize_minval == 0.0f &&
466
quantize_maxval == 0.0f ) )
468
value0 = ROUND(quantize_zeroval + value0 * (quantize_oneval - quantize_zeroval) + dither_val );
469
value0 = CLAMP(value0, quantize_minval, quantize_maxval) ;
470
value1 = ROUND(quantize_zeroval + value1 * (quantize_oneval - quantize_zeroval) + dither_val );
471
value1 = CLAMP(value1, quantize_minval, quantize_maxval) ;
472
value2 = ROUND(quantize_zeroval + value2 * (quantize_oneval - quantize_zeroval) + dither_val );
473
value2 = CLAMP(value2, quantize_minval, quantize_maxval) ;
474
alpha = ROUND(quantize_zeroval + alpha * (quantize_oneval - quantize_zeroval) + dither_val );
475
alpha = CLAMP(alpha, quantize_minval, quantize_maxval) ;
477
else if ( g_Format != DataFormat_Unsigned8 )
479
// If we are displaying an FP image we will need to quantize ourselves.
486
// C� = INT_PRELERP( A�, B�, b, t )
490
int A = INT_PRELERP( g_Image[ so + 0 ], value0, alpha, t );
491
int B = INT_PRELERP( g_Image[ so + 1 ], value1, alpha, t );
492
int C = INT_PRELERP( g_Image[ so + 2 ], value2, alpha, t );
493
g_Image[ so + 0 ] = CLAMP( A, 0, 255 );
494
g_Image[ so + 1 ] = CLAMP( B, 0, 255 );
495
g_Image[ so + 2 ] = CLAMP( C, 0, 255 );
498
bucket += message->m_ElementSize;
502
//std::cerr << message->m_XMin << ", " << message->m_YMin << " - " << message->m_XMaxPlus1 << ", " << message->m_YMaxPlus1 << std::endl;
504
const TqInt BucketX = message->m_XMin - g_CWXmin;
505
const TqInt BucketY = g_ImageHeight - ( message->m_YMaxPlus1 - g_CWYmin );
506
const TqInt BucketW = message->m_XMaxPlus1 - message->m_XMin;
507
const TqInt BucketH = message->m_YMaxPlus1 - message->m_YMin;
509
glEnable( GL_SCISSOR_TEST );
510
glScissor( BucketX, BucketY, BucketW, BucketH );
513
g_PixelsProcessed += (BucketW * BucketH);
514
glutSetWindowTitle(get_window_title().c_str());
519
TqInt Close( SOCKET s, SqDDMessageBase* pMsgB )
522
if ( DDSendMsg( s, &closeack ) <= 0 )
528
TqInt Abandon( SOCKET s, SqDDMessageBase* pMsgB )
533
TqInt HandleMessage( SOCKET s, SqDDMessageBase* pMsgB )
535
switch ( pMsgB->m_MessageID )
537
case MessageID_Filename:
539
SqDDMessageFilename * message = static_cast<SqDDMessageFilename*>( pMsgB );
540
g_Filename = message->m_String;
544
case MessageID_UserParam:
546
SqDDMessageUserParam * pMsg = static_cast<SqDDMessageUserParam*>( pMsgB );
547
// Check if we understand the parameter.
548
if( strncmp( pMsg->m_NameAndData, "quantize", pMsg->m_NameLength ) == 0 )
550
TqFloat* quantize = reinterpret_cast<TqFloat*>( &pMsg->m_NameAndData[ pMsg->m_NameLength + 1 ] );
551
quantize_zeroval = quantize[0];
552
quantize_oneval = quantize[1];
553
quantize_minval = quantize[2];
554
quantize_maxval = quantize[3];