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