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