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