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