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