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