d5b580b60bfa1932ffa3debe2f557f0e60b7d39a
[occt.git] / src / OpenGl / OpenGl_togl_print.cxx
1 /* File         OpenGL_togl_print.c 
2 Created      March 2000
3 Author       THA
4 e-mail t-hartl@muenchen.matra-dtv.fr  */
5
6 #define RIC120302       /* GG Enable to use the application display
7 //                      callback at end of traversal
8 */
9
10 #include <OpenGl_tgl_all.hxx>
11 #include <InterfaceGraphic_Graphic3d.hxx>
12 #include <InterfaceGraphic_Visual3d.hxx>
13 #include <OpenGl_tsm_ws.hxx>
14 #include <OpenGl_tgl.hxx>
15 #include <OpenGl_txgl.hxx>
16 #include <OpenGl_tgl_tox.hxx>
17 #include <OpenGl_tgl_funcs.hxx>
18 #include <OpenGl_tgl_subrvis.hxx>
19 #include <OpenGl_animation.hxx>
20 #include <OpenGl_FrameBuffer.hxx>
21 #include <OpenGl_PrinterContext.hxx>
22 #include <Visual3d_Layer.hxx>
23 #include <TColStd_Array2OfReal.hxx>
24 #include <string.h>
25
26 #ifdef HAVE_FREEIMAGE
27   #include <NCollection_Handle.hxx>
28   #include <FreeImagePlus.h>
29   #ifdef _MSC_VER
30   #pragma comment( lib, "FreeImage.lib" )
31   #pragma comment( lib, "FreeImagePlus.lib" )
32   #endif
33   typedef NCollection_Handle<fipImage> FipHandle;
34 #endif
35
36 // ---------------------------------------------------------------
37 // Function: getNearestPowOfTwo
38 // Purpose:  get the nearest power of two for theNumber
39 // ---------------------------------------------------------------
40 static GLsizei getNearestPowOfTwo (const GLsizei theNumber)
41 {
42   GLsizei aLast = 1;
43   for (GLsizei p2 = 1; p2 <= theNumber; aLast = p2, p2 <<= 1);
44   return aLast;
45 }
46
47 // ---------------------------------------------------------------
48 // Function: getMaxFrameSize
49 // Purpose:  get the maximum possible frame size
50 // ---------------------------------------------------------------
51 static void getMaxFrameSize(Standard_Integer& theWidth,
52                             Standard_Integer& theHeight)
53 {
54   GLsizei aMaxX, aMaxY;
55   GLint aVpDim[2];
56   GLint aTexDim = 2048;
57   glGetIntegerv (GL_MAX_VIEWPORT_DIMS, (GLint*) &aVpDim);
58   glGetIntegerv (GL_MAX_TEXTURE_SIZE, &aTexDim);
59   (aVpDim[0] >= aTexDim) ? aMaxX = (GLsizei) aTexDim : 
60                            aMaxX = getNearestPowOfTwo((GLsizei)aVpDim[0]);
61   (aVpDim[1] >= aTexDim) ? aMaxY = (GLsizei) aTexDim :
62                            aMaxY = getNearestPowOfTwo((GLsizei)aVpDim[1]);
63
64   theWidth  = (Standard_Integer)aMaxX;
65   theHeight = (Standard_Integer)aMaxY;
66 }
67
68 // ---------------------------------------------------------------
69 // Function: fitDimensionsRatio
70 // Purpose:  calculate correct width/height ratio for theWidth and
71 //           theHeight parameters
72 // ---------------------------------------------------------------
73 static void fitDimensionsRatio (Standard_Integer& theWidth,
74                                 Standard_Integer& theHeight,
75                                 const Standard_Real theViewRatio)
76 {
77   // set dimensions in accordance with the viewratio
78   if (theHeight <  theWidth/theViewRatio)
79       theWidth  = (Standard_Integer)(theHeight*theViewRatio);
80
81   if (theWidth  <  theHeight*theViewRatio)
82       theHeight = (Standard_Integer)(theWidth/theViewRatio);
83 }
84
85 // ---------------------------------------------------------------
86 // Function: getDimensionsTiling
87 // Purpose:  calculate maximum possible dimensions for framebuffer 
88 //           in tiling mode according to the view size
89 // ---------------------------------------------------------------
90 static void getDimensionsTiling (Standard_Integer& theFrameWidth,
91                                  Standard_Integer& theFrameHeight,
92                                  const int theViewWidth,
93                                  const int theViewHeight)
94 {
95   // fit the maximum dimensions into the printing area
96   if (theFrameWidth > theViewWidth)
97       theFrameWidth = theViewWidth;
98
99   if (theFrameHeight > theViewHeight)
100       theFrameHeight = theViewHeight;
101 }
102
103 // ---------------------------------------------------------------
104 // Function: initBufferStretch
105 // Purpose:  calculate initialization sizes for frame buffer
106 //           when the stretch algorithm is selected
107 // ---------------------------------------------------------------
108 static void initBufferStretch (Standard_Integer& theFrameWidth,
109                                Standard_Integer& theFrameHeight,
110                                const int theViewWidth,
111                                const int theViewHeight)
112 {
113
114   // Calculate correct width/height for framebuffer
115   Standard_Real aViewRatio = (Standard_Real)theViewWidth/theViewHeight;
116   fitDimensionsRatio (theFrameWidth, theFrameHeight, aViewRatio);
117
118   // downscale the framebuffer if it is too large
119   Standard_Real aWidthRate  = (Standard_Real)theFrameWidth /theViewWidth;
120   Standard_Real aHeightRate = (Standard_Real)theFrameHeight/theViewHeight;
121
122   if ((aWidthRate > 1 && aHeightRate > 1 && aWidthRate >= aHeightRate) || 
123       (aWidthRate > 1 && aHeightRate <= 1))
124   {
125     theFrameWidth  = (Standard_Integer)(theFrameWidth /aWidthRate);
126     theFrameHeight = (Standard_Integer)(theFrameHeight/aWidthRate);
127   }
128   else if ((aWidthRate  > 1 && aHeightRate > 1 && aWidthRate < aHeightRate) ||
129            (aWidthRate <= 1 && aHeightRate > 1))
130   {
131     theFrameWidth  = (Standard_Integer)(theFrameWidth /aHeightRate);
132     theFrameHeight = (Standard_Integer)(theFrameHeight/aHeightRate);
133   }
134
135 }
136
137 // ---------------------------------------------------------------
138 // Function: initBufferTiling
139 // Purpose:  calculate initialization sizes for frame buffer
140 //           when the tile algorithm is selected
141 // ---------------------------------------------------------------
142 static void initBufferTiling (Standard_Integer& theFrameWidth,
143                               Standard_Integer &theFrameHeight,
144                               const int theViewWidth,
145                               const int theViewHeight)
146 {
147   // fit framebuffer into the printing area
148   if (theFrameWidth > theViewWidth)
149       theFrameWidth = theViewWidth;
150
151   if (theFrameHeight > theViewHeight)
152       theFrameHeight = theViewHeight;
153 }
154
155 // ---------------------------------------------------------------
156 // Function: redrawView
157 // Purpose:  redraw view in printing mode
158 // ---------------------------------------------------------------
159 static void redrawView (CALL_DEF_VIEW *aview, 
160                         CALL_DEF_LAYER *anunderlayer, 
161                         CALL_DEF_LAYER *anoverlayer,
162                         const int isBackground)
163 {
164   // prepare for redraw
165   call_func_redraw_all_structs_begin (aview->WsId);
166   call_togl_setplane (aview);
167
168   // clear background
169   if (isBackground == 0)
170   {
171     glClearColor (1.0, 1.0, 1.0, 1.0);
172     glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
173   }
174
175   // draw underlayer
176   if (anunderlayer->ptrLayer)
177   {
178     call_togl_redraw_layer2d (aview, anunderlayer);
179   }
180
181   // redraw main plane
182   call_func_redraw_all_structs_proc (aview->WsId);
183   call_subr_displayCB (aview,OCC_REDRAW_BITMAP | OCC_PRE_OVERLAY);
184   // draw overlayer
185   if (anoverlayer->ptrLayer)
186   {
187     call_togl_redraw_layer2d (aview, anoverlayer);
188   }
189   call_subr_displayCB (aview,OCC_REDRAW_BITMAP);
190
191
192   // tell to end redrawing
193   call_func_redraw_all_structs_end (aview->WsId, 0);
194   call_togl_redraw_immediat_mode (aview);
195 }
196
197 // ---------------------------------------------------------------
198 // Function: initBitmapBuffer
199 // Purpose:  init device independent bitmap to hold printing data
200 // ---------------------------------------------------------------
201 #ifdef WNT
202 #ifndef HAVE_FREEIMAGE
203 static void initBitmapBuffer (const HDC theMemoryDC,
204                               HBITMAP &theMemoryBmp,
205                               const   Standard_Integer theBmpWidth,
206                               const   Standard_Integer theBmpHeight,
207                               VOID*   &theBufferPtr)
208 {
209   // define compatible bitmap
210   BITMAPINFO aBitmapData;
211   memset (&aBitmapData, 0, sizeof (BITMAPINFOHEADER));
212   aBitmapData.bmiHeader.biSize          = sizeof (BITMAPINFOHEADER);
213   aBitmapData.bmiHeader.biWidth         = theBmpWidth;
214   aBitmapData.bmiHeader.biHeight        = theBmpHeight;
215   aBitmapData.bmiHeader.biPlanes        = 1;
216   aBitmapData.bmiHeader.biBitCount      = 24;
217   aBitmapData.bmiHeader.biXPelsPerMeter = 0;
218   aBitmapData.bmiHeader.biYPelsPerMeter = 0;
219   aBitmapData.bmiHeader.biClrUsed       = 0;
220   aBitmapData.bmiHeader.biClrImportant  = 0;
221   aBitmapData.bmiHeader.biCompression   = BI_RGB;
222   aBitmapData.bmiHeader.biSizeImage     = 0;
223
224   // Create Device Independent Bitmap
225   theMemoryBmp = CreateDIBSection (theMemoryDC, &aBitmapData, DIB_RGB_COLORS,
226                                    &theBufferPtr, NULL, 0);
227 }
228 #else
229 // ---------------------------------------------------------------
230 // Function: imagePasteDC
231 // Purpose:  copy the data from image buffer to the device context
232 // ---------------------------------------------------------------
233 static bool imagePasteDC(HDC theDstDC,    FipHandle theImage, int theOffsetX,
234                          int theOffsetY,  int theWidth, int theHeight, 
235                          int theLeft = 0, int theTop = 0)
236 {
237   // get image parameters
238   BITMAPINFO* aBitmapData = theImage->getInfo ();
239   SetStretchBltMode (theDstDC, STRETCH_HALFTONE);
240  
241   // organize blocks data passing if memory isn't enough to pass all the data
242   // at once
243   int aLinesComplete = 0, aMaxBlockWidth = theHeight, aBlockWidth = 0,
244       aPassed        = 0, aInverseLine   = 0, aScan = 0;
245   BYTE *aDataPtr = 0;
246   while (aMaxBlockWidth >= 1 && aLinesComplete < theHeight)
247   {
248     // how much lines still to pass
249     aBlockWidth = theHeight - aLinesComplete;
250
251     // normalize count of lines to pass to maximum lines count at one pass.
252     if (aBlockWidth > aMaxBlockWidth)
253       aBlockWidth = aMaxBlockWidth;
254
255     // access image data at the start scan line, we need to calculate scan from
256     // the bottom of image (image is bottom-left, the src coord is top-left)
257     aInverseLine = theTop + aBlockWidth + aLinesComplete;
258     aScan = theImage->getHeight() - aInverseLine;
259     aDataPtr = theImage->getScanLine (aScan);
260     if (!aDataPtr)
261       return false;
262
263     // try to pass block to the device
264     if (aBlockWidth > 0)
265     {
266       // instead of banded output we provide blocked as it isn't always passed
267       // to printer as it is expected
268       aPassed = SetDIBitsToDevice (theDstDC, theOffsetX,
269                                    theOffsetY + aLinesComplete,
270                                    theWidth, aBlockWidth, theLeft, 0,
271                                    0, aBlockWidth,
272                                    aDataPtr, aBitmapData, DIB_RGB_COLORS);
273
274       // if result is bad, try to decrease band width
275       if (aPassed != aBlockWidth)
276       {
277         aMaxBlockWidth = aMaxBlockWidth >> 1;
278         aLinesComplete = 0;
279       }
280       else
281         aLinesComplete += aBlockWidth;
282     }
283   }
284
285   // check for total failure
286   if (aMaxBlockWidth < 1)
287     return false;
288
289   return true;
290 }
291
292 // ---------------------------------------------------------------
293 // Function: imageStretchDC
294 // Purpose:  copy pixels from image to dc by stretching them
295 // ---------------------------------------------------------------
296 static bool imageStretchDC(HDC theDstDC,   FipHandle theImage, int theOffsetX,
297                            int theOffsetY, int theWidth, int theHeight)
298 {
299   // access to raw image data
300   BYTE *aDataPtr = theImage->accessPixels ();
301   if (!aDataPtr)
302     return false;
303
304   // get image parameters
305   unsigned int widthPx    = theImage->getWidth ();
306   unsigned int heightPx   = theImage->getHeight ();
307   BITMAPINFO* aBitmapData = theImage->getInfo ();
308   SetStretchBltMode (theDstDC, STRETCH_HALFTONE);
309   
310   // pass lines and check if operation is succesfull
311   int aPassed = 0;
312   aPassed = StretchDIBits (theDstDC, theOffsetX, theOffsetY, theWidth,
313                            theHeight, 0, 0, widthPx, heightPx, aDataPtr,
314                            aBitmapData, DIB_RGB_COLORS, SRCCOPY);
315
316   if (aPassed != heightPx)
317     return false;
318  
319   return true;
320 }
321 #endif
322 #endif
323
324 // ---------------------------------------------------------------
325 // Function: call_togl_print
326 // Purpose:
327 // ---------------------------------------------------------------
328 Standard_Boolean EXPORT
329 call_togl_print
330 (
331  CALL_DEF_VIEW *aview,
332  CALL_DEF_LAYER *anunderlayer,
333  CALL_DEF_LAYER *anoverlayer,
334  const Aspect_Drawable hPrintDC,
335  const int background,
336  const char* filename,
337  const int   printalgo,
338  const float theScaleFactor
339  )
340 {
341
342 #ifdef WNT
343
344   CMN_KEY_DATA data; 
345   DOCINFO di;
346   bool IsTiling = (printalgo == 1);
347   HDC  hPrnDC   = (HDC) hPrintDC;
348
349   TsmGetWSAttri (aview->WsId, WSWindow, &data);
350   if (TxglWinset (call_thedisplay, (WINDOW) data.ldata) != TSuccess)
351   {
352     MessageBox (NULL, "Print failed: can't setup the view for printing.",
353                 "The operation couldn't be completed.", MB_OK);
354     return Standard_False;
355   }
356
357   // printer page dimensions
358   int devWidth  = GetDeviceCaps (hPrnDC, HORZRES);
359   int devHeight = GetDeviceCaps (hPrnDC, VERTRES);
360
361   // if context is actually a memory dc, try to retrieve bitmap dimensions
362   // (memory dc could be used for testing purposes)
363   if (GetObjectType (hPrnDC) == OBJ_MEMDC)
364   {
365     // memory dc dimensions
366     BITMAP aBitmapInfo;
367     HBITMAP aMemoryBitmap = (HBITMAP) GetCurrentObject (hPrnDC, OBJ_BITMAP);
368     if (aMemoryBitmap)
369       if (GetObject (aMemoryBitmap, sizeof (BITMAP), &aBitmapInfo))
370       {
371         devWidth  = aBitmapInfo.bmWidth;
372         devHeight = aBitmapInfo.bmHeight;
373       }
374   }
375
376   Standard_Integer tempWidth  = (Standard_Integer) devWidth;
377   Standard_Integer tempHeight = (Standard_Integer) devHeight;
378
379   // view dimensions
380   RECT rect;
381   GetClientRect((WINDOW) data.ldata, &rect);
382   int viewWidth  = rect.right-rect.left;
383   int viewHeight = rect.bottom-rect.top;
384   if (viewWidth == 0 || viewHeight == 0)
385   {
386     MessageBox (NULL, "Print failed: can't setup the view for printing.",
387                 "The operation couldn't be completed.", MB_OK);
388     return Standard_False;
389   }
390
391   // calculate correct width/height ratio
392   Standard_Real viewRatio = (Standard_Real)viewWidth/viewHeight;
393   fitDimensionsRatio(tempWidth, tempHeight, viewRatio);
394
395   // width and height for printing area
396   int width  = (int) (tempWidth  * theScaleFactor);
397   int height = (int) (tempHeight * theScaleFactor);
398
399   // device independent bitmap for the whole view
400 #ifdef HAVE_FREEIMAGE
401   FipHandle  aViewImage  = NULL;
402   BYTE*      aViewBuffer = NULL;
403 #else
404   HDC     hMemDC          = CreateCompatibleDC (hPrnDC);
405   HBITMAP hViewBitmap     = NULL;
406   HGDIOBJ hViewBitmapOld  = NULL;
407   VOID*   aViewBuffer    = NULL;
408 #endif
409
410   // Frame buffer initialization
411   OpenGl_FrameBuffer* aFrameBuffer = NULL;
412   OpenGl_FrameBuffer* aPrevBuffer = (OpenGl_FrameBuffer*) aview->ptrFBO;
413   Standard_Integer aFrameWidth (0),  aFrameHeight (0),
414                    aPrevBufferX (0), aPrevBufferY (0);
415
416   // try to use existing frame buffer
417   if (aPrevBuffer)
418   {
419     GLsizei aPrevWidth  = aPrevBuffer->GetSizeX();
420     GLsizei aPrevHeight = aPrevBuffer->GetSizeY();
421     bool isUsable = false;
422
423     // check if its possible to use previous frame buffer
424     if (!IsTiling && aPrevWidth >= width && aPrevHeight >= height)
425     {
426       aFrameWidth  = (Standard_Integer) width;
427       aFrameHeight = (Standard_Integer) height;
428       isUsable = true;
429     }
430     else if (IsTiling)
431     {
432       getDimensionsTiling (aFrameWidth, aFrameHeight, width, height);
433       if (aPrevWidth >= aFrameWidth && aPrevHeight >= aFrameHeight)
434         isUsable = true;
435     }
436
437     // if it is enough memory for image paste dc operation
438     if (isUsable)
439     {
440 #ifdef HAVE_FREEIMAGE
441       // try to allocate fipImage and necessary resources
442       fipImage* anImagePtr = new fipImage (FIT_BITMAP, aFrameWidth,
443                                            aFrameHeight, 24);
444
445       // if allocated succesfully
446       if (anImagePtr->isValid())
447       {
448         aViewImage  = anImagePtr;
449         aViewBuffer = aViewImage->accessPixels ();
450       }
451       else
452         delete anImagePtr;
453
454       if (!aViewBuffer)
455       {
456         isUsable = false;
457         aViewBuffer = NULL;
458         aViewImage  = NULL;
459       }
460 #else
461       // try to allocate compatible bitmap and necessary resources
462       initBitmapBuffer (hMemDC, hViewBitmap, 
463                         aFrameWidth, aFrameHeight, aViewBuffer);
464       if (!aViewBuffer)
465       {
466         isUsable = false;
467         if (hViewBitmap)
468           DeleteObject (hViewBitmap);
469         hViewBitmap = NULL;
470       }
471       else
472         hViewBitmapOld = SelectObject (hMemDC, hViewBitmap);
473 #endif
474     }
475
476     // use previous frame buffer
477     if (isUsable)
478     {
479       aPrevBufferX = aPrevWidth;
480       aPrevBufferY = aPrevHeight;
481       aFrameBuffer = aPrevBuffer;
482       aFrameBuffer->ChangeViewport (aFrameWidth, aFrameHeight);
483     }
484   }
485
486   // if previous buffer cannot be used, try to init a new one
487   if (!aFrameBuffer)
488   {
489     aFrameBuffer = new OpenGl_FrameBuffer();
490
491     // try to create the framebuffer with the best possible size
492     Standard_Integer aMaxWidth(0), aMaxHeight(0);
493     getMaxFrameSize (aMaxWidth, aMaxHeight);
494     while (aMaxWidth > 1 && aMaxHeight > 1)
495     {
496       aFrameWidth  = aMaxWidth;
497       aFrameHeight = aMaxHeight;
498
499       // calculate dimensions for different printing algorithms
500       if (!IsTiling)
501         initBufferStretch (aFrameWidth, aFrameHeight, width, height);
502       else
503         initBufferTiling (aFrameWidth, aFrameHeight, width, height);
504
505       // try to initialize framebuffer
506       if (aFrameBuffer->Init (aFrameWidth, aFrameHeight))
507       {
508 #ifdef HAVE_FREEIMAGE
509         // try to allocate fipImage and necessary resources
510         fipImage* anImagePtr = new fipImage (FIT_BITMAP, aFrameWidth,
511                                             aFrameHeight, 24);
512
513         // if allocated succesfully
514         if (anImagePtr->isValid())
515         {
516           aViewImage  = anImagePtr;
517           aViewBuffer = aViewImage->accessPixels ();
518         }
519         else
520           delete anImagePtr;
521
522         if (!aViewBuffer)
523         {
524           aFrameBuffer->Release ();
525           aViewBuffer = NULL;
526           aViewImage  = NULL;
527         }
528         else
529           break;
530 #else
531         // try to allocate compatible bitmap and necessary resources
532         initBitmapBuffer (hMemDC, hViewBitmap, 
533                           aFrameWidth, aFrameHeight, aViewBuffer);
534         if (!aViewBuffer)
535         {
536           if (hViewBitmap)
537             DeleteObject (hViewBitmap);
538           aFrameBuffer->Release ();
539           hViewBitmap = NULL;
540         }
541         else
542         {
543           hViewBitmapOld = SelectObject (hMemDC, hViewBitmap);
544           break;
545         }
546 #endif
547       }
548
549       // not initialized, decrease dimensions
550       aMaxWidth  = aMaxWidth  >> 1;
551       aMaxHeight = aMaxHeight >> 1;
552     }
553
554     // check if there are proper dimensions 
555     if (aMaxWidth <= 1 || aMaxHeight <= 1)
556     {
557       MessageBox (NULL, "Print failed: can't allocate buffer for printing.",
558                   "The operation couldn't be completed.", MB_OK);
559
560       if (aFrameBuffer)
561         delete aFrameBuffer;
562 #ifndef HAVE_FREEIMAGE
563       if (hMemDC)
564         DeleteDC (hMemDC);
565 #endif
566
567       return Standard_False;
568     }
569   }
570
571   // setup printing context and viewport
572   GLint aViewPortBack[4]; 
573   GLint aReadBufferPrev = GL_BACK;
574   GLint anAlignBack     = 1;
575   OpenGl_PrinterContext aPrinterContext (GET_GL_CONTEXT());
576   aPrinterContext.SetLayerViewport ((GLsizei)aFrameWidth,
577                                     (GLsizei)aFrameHeight);
578   glGetIntegerv (GL_VIEWPORT, aViewPortBack);
579   glGetIntegerv (GL_PACK_ALIGNMENT, &anAlignBack);
580   glPixelStorei (GL_PACK_ALIGNMENT, 4);
581
582   // start document if the printer context is not actually a memory dc
583   // (memory dc could be used for testing purposes)
584   if (GetObjectType (hPrnDC) == OBJ_DC)
585   {
586     // Initalize printing procedure
587     di.cbSize = sizeof(DOCINFO);
588     di.lpszDocName = "Open Cascade Document - print v3d view";
589     di.lpszOutput = filename;
590
591     // if can't print the document
592     if (StartDoc (hPrnDC, &di) <= 0 || StartPage (hPrnDC) <= 0)
593     {
594       MessageBox (NULL, "Print failed: printer can't start operation.",
595                   "The operation couldn't be completed.", MB_OK);
596 #ifndef HAVE_FREEIMAGE
597       if (hViewBitmap)
598       {
599         SelectObject (hMemDC, hViewBitmapOld);
600         DeleteObject (hViewBitmap);
601       }
602       DeleteDC (hMemDC);
603 #endif
604
605       return Standard_False;
606     }
607   }
608
609   // activate the offscreen buffer
610   aFrameBuffer->BindBuffer ();
611
612   // calculate offset for centered printing
613   int aDevOffx = (int)(devWidth  - width) /2;
614   int aDevOffy = (int)(devHeight - height)/2;
615
616   // operation complete flag
617   bool isDone = true;
618   
619   if (!IsTiling)
620   {
621     aPrinterContext.SetScale ((GLfloat)aFrameWidth /viewWidth,
622                               (GLfloat)aFrameHeight/viewHeight);
623     aFrameBuffer->SetupViewport ();
624     redrawView (aview, anunderlayer, anoverlayer, background);
625     glReadPixels (0, 0, aFrameWidth, aFrameHeight,
626                   GL_BGR_EXT, GL_UNSIGNED_BYTE, (GLvoid* )aViewBuffer);
627
628     // copy result to the printer device and check for errors
629 #ifdef HAVE_FREEIMAGE
630     if (!aViewImage->rescale(width, height, FILTER_BICUBIC) ||
631         !imagePasteDC (hPrnDC, aViewImage, aDevOffx, aDevOffy, width, height))
632       isDone = imageStretchDC (hPrnDC, aViewImage, aDevOffx, aDevOffy,
633                                width, height);
634 #else
635     if (width > aFrameWidth && height > aFrameHeight)
636     {
637       SetStretchBltMode (hPrnDC, STRETCH_HALFTONE);
638       isDone = StretchBlt (hPrnDC, aDevOffx, aDevOffy, width, height,
639                            hMemDC, 0, 0, aFrameWidth, aFrameHeight, SRCCOPY);
640     }
641     else
642     {
643       isDone = BitBlt (hPrnDC, aDevOffx, aDevOffy, width, height,
644                        hMemDC, 0, 0, SRCCOPY);
645     }
646 #endif
647   }
648   else
649   {
650     // calculate total count of frames and cropping size
651     Standard_Integer aPxCropx = 0;
652     Standard_Integer aPxCropy = 0;
653     Standard_Integer aTotalx = 
654                      (Standard_Integer)floor ((float)width /aFrameWidth);
655     Standard_Integer aTotaly = 
656                      (Standard_Integer)floor ((float)height/aFrameHeight);
657     if (width %aFrameWidth != 0)
658     {
659       aPxCropx = (aFrameWidth - width%aFrameWidth)/2;
660       aTotalx++;
661     }
662     if (height%aFrameHeight != 0)
663     {
664       aPxCropy = (aFrameHeight - height%aFrameHeight)/2;
665       aTotaly++;
666     }
667
668     int anOddPixelx = (width %aFrameWidth) %2;
669     int anOddPixely = (height%aFrameHeight)%2;
670
671     // calculate scale factor for full frames
672     Standard_Real aScalex = (Standard_Real)width /aFrameWidth;
673     Standard_Real aScaley = (Standard_Real)height/aFrameHeight;
674
675     // calculate and set the text scaling factor for printing context
676     GLfloat aScaleRatex = (GLfloat)aFrameWidth /viewWidth;
677     GLfloat aScaleRatey = (GLfloat)aFrameHeight/viewHeight;
678     aPrinterContext.SetScale (aScaleRatex*(GLfloat)aScalex,
679                               aScaleRatey*(GLfloat)aScaley);
680
681     // initialize projection matrix for printer context
682     TColStd_Array2OfReal aProj (0, 3, 0, 3);
683     Standard_Real aDef = 0;
684     aProj.Init (aDef);
685     aProj(2,2) = 1.0;
686     aProj(3,3) = 1.0;
687
688     // projection matrix offsets for printer context
689     // offsets are even numbers
690     Standard_Real aOffsetx(0), aOffsety(0);
691     aOffsetx = -(aTotalx-1);
692     aOffsety = -(aTotaly-1);
693
694     // rect of frame image that will be copied
695     // and coordinates in view image where to put it
696     Standard_Integer aLeft = 0, aRight = 0, aBottom = 0, aTop = 0;
697     Standard_Integer aSubLeft = (Standard_Integer)aDevOffx;
698     Standard_Integer aSubTop  = (Standard_Integer)aDevOffy;
699
700     // draw sequence of full frames
701     for (int i = 0; i < aTotalx; i++)
702     {
703       // offsets are even numbers
704       aOffsety = -(aTotaly-1);
705       aSubTop  =  (Standard_Integer)aDevOffy;
706
707       // calculate cropped frame rect
708       aLeft  = (i == 0) ? aPxCropx : 0;
709       aRight = (i == aTotalx-1) ? aFrameWidth-(aPxCropx+anOddPixelx) :
710                                   aFrameWidth;
711
712       for (int j = 0; j < aTotaly; j++)
713       {
714         // no offset for single frames
715         aProj(3,0) = (aTotalx == 1) ? 0 : -aOffsetx;
716         aProj(3,1) = (aTotaly == 1) ? 0 :  aOffsety;
717
718         // set projection matrix
719         aProj(0,0) = aScalex;
720         aProj(1,1) = aScaley;
721         aPrinterContext.SetProjTransformation (aProj);
722
723         // calculate cropped frame rect
724         aTop    = (j == 0)         ? aPxCropy : 0;
725         aBottom = (j == aTotaly-1) ? aFrameHeight-(aPxCropy+anOddPixely) :
726                                      aFrameHeight;
727
728         // draw to the offscreen buffer and capture the result
729         aFrameBuffer->SetupViewport ();
730         redrawView (aview, anunderlayer, anoverlayer, background);
731         glReadPixels (0, 0, aFrameWidth, aFrameHeight,
732                       GL_BGR_EXT, GL_UNSIGNED_BYTE, (GLvoid* )aViewBuffer);
733 #ifdef HAVE_FREEIMAGE
734         // cut out pixels that are out of printing area
735         isDone = imagePasteDC (hPrnDC, aViewImage, aSubLeft, aSubTop,
736                                aRight-aLeft, aBottom-aTop, aLeft, aTop);
737 #else
738         isDone = BitBlt (hPrnDC, aSubLeft, aSubTop, aRight-aLeft, aBottom-aTop,
739                          hMemDC, aLeft, aTop, SRCCOPY);
740 #endif
741
742         // stop operation if errors
743         if (!isDone)
744           break;
745
746         // calculate new view offset for y-coordinate
747         aOffsety += 2.0;
748         aSubTop  += aBottom-aTop;
749       }
750
751       // stop operation if errors
752       if (!isDone)
753         break;
754  
755       // calculate new view offset for x-coordinate
756       aOffsetx += 2.0;
757       aSubLeft += aRight-aLeft;
758     }
759   }
760
761   // complete printing or indicate an error
762   if (GetObjectType (hPrnDC) == OBJ_DC && isDone == true)
763   {
764     EndPage (hPrnDC);
765     EndDoc (hPrnDC);
766   }
767   else if (isDone == false)
768   {
769     MessageBox (NULL, "Print failed: insufficient memory or spool error.\nPlease use smaller printer resolution.",
770                 "The opeartion couldn't be completed.", MB_OK);
771     if (GetObjectType (hPrnDC) == OBJ_DC)
772       AbortDoc (hPrnDC);
773   }
774   
775   // return OpenGl to the previous state
776   aPrinterContext.Deactivate ();
777   glPixelStorei (GL_PACK_ALIGNMENT, anAlignBack);
778   aFrameBuffer->UnbindBuffer();
779   glViewport (aViewPortBack[0], aViewPortBack[1], 
780               aViewPortBack[2], aViewPortBack[3]);
781   if (aPrevBuffer)
782     aPrevBuffer->ChangeViewport (aPrevBufferX, aPrevBufferY);
783   else
784     delete aFrameBuffer;
785
786   // delete resources
787 #ifndef HAVE_FREEIMAGE
788   if (hViewBitmap)
789   {
790     SelectObject (hMemDC, hViewBitmapOld);
791     DeleteObject (hViewBitmap);
792   }
793   DeleteDC (hMemDC);
794 #endif
795
796   return (Standard_Boolean) isDone;
797
798 #else // not WNT
799   return Standard_False;
800 #endif 
801 }
802