9fd2549703cd5626c37a0cdf3492e7279ef7e966
[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 // Function: getDimensionsTiling
220 // Purpose:  calculate maximum possible dimensions for framebuffer
221 //           in tiling mode according to the view size
222 // ---------------------------------------------------------------
223 static void getDimensionsTiling (Standard_Integer& theFrameWidth,
224                                  Standard_Integer& theFrameHeight,
225                                  const int theViewWidth,
226                                  const int theViewHeight)
227 {
228   // fit the maximum dimensions into the printing area
229   if (theFrameWidth > theViewWidth)
230       theFrameWidth = theViewWidth;
231
232   if (theFrameHeight > theViewHeight)
233       theFrameHeight = theViewHeight;
234 }
235 // ---------------------------------------------------------------
236 // Function: initBufferStretch
237 // Purpose:  calculate initialization sizes for frame buffer
238 //           when the stretch algorithm is selected
239 // ---------------------------------------------------------------
240 static void initBufferStretch (Standard_Integer& theFrameWidth,
241                                Standard_Integer& theFrameHeight,
242                                const int theViewWidth,
243                                const int theViewHeight)
244 {
245
246   // Calculate correct width/height for framebuffer
247   Standard_Real aViewRatio = (Standard_Real)theViewWidth/theViewHeight;
248   fitDimensionsRatio (theFrameWidth, theFrameHeight, aViewRatio);
249
250   // downscale the framebuffer if it is too large
251   Standard_Real aWidthRate  = (Standard_Real)theFrameWidth /theViewWidth;
252   Standard_Real aHeightRate = (Standard_Real)theFrameHeight/theViewHeight;
253
254   if ((aWidthRate > 1 && aHeightRate > 1 && aWidthRate >= aHeightRate) ||
255       (aWidthRate > 1 && aHeightRate <= 1))
256   {
257     theFrameWidth  = (Standard_Integer)(theFrameWidth /aWidthRate);
258     theFrameHeight = (Standard_Integer)(theFrameHeight/aWidthRate);
259   }
260   else if ((aWidthRate  > 1 && aHeightRate > 1 && aWidthRate < aHeightRate) ||
261            (aWidthRate <= 1 && aHeightRate > 1))
262   {
263     theFrameWidth  = (Standard_Integer)(theFrameWidth /aHeightRate);
264     theFrameHeight = (Standard_Integer)(theFrameHeight/aHeightRate);
265   }
266 }
267 // ---------------------------------------------------------------
268 // Function: initBufferTiling
269 // Purpose:  calculate initialization sizes for frame buffer
270 //           when the tile algorithm is selected
271 // ---------------------------------------------------------------
272 static void initBufferTiling (Standard_Integer& theFrameWidth,
273                               Standard_Integer &theFrameHeight,
274                               const int theViewWidth,
275                               const int theViewHeight)
276 {
277   // fit framebuffer into the printing area
278   if (theFrameWidth > theViewWidth)
279       theFrameWidth = theViewWidth;
280
281   if (theFrameHeight > theViewHeight)
282       theFrameHeight = theViewHeight;
283 }
284 #endif
285
286 // ---------------------------------------------------------------
287 // ---------------------------------------------------------------
288
289 //call_togl_print
290
291 Standard_Boolean OpenGl_Workspace::Print
292   (const Handle(OpenGl_PrinterContext)& thePrintContext,
293    const Graphic3d_CView& ACView,
294    const Aspect_CLayer2d& ACUnderLayer,
295    const Aspect_CLayer2d& ACOverLayer,
296    const Aspect_Handle    hPrintDC,// const Aspect_Drawable hPrintDC,
297    const Standard_Boolean showBackground,
298    const Standard_CString filename,
299    const Aspect_PrintAlgo printAlgorithm,
300    const Standard_Real theScaleFactor)
301 {
302   if (thePrintContext.IsNull())
303   {
304     return Standard_False;
305   }
306
307 #ifdef _WIN32
308
309   if (!Activate())
310   {
311     //MessageBox (NULL, "Print failed: can't setup the view for printing.",
312     //            "The operation couldn't be completed.", MB_OK);
313     return Standard_False;
314   }
315
316   // printer page dimensions
317   HDC hPrnDC = (HDC) hPrintDC;
318   int devWidth  = GetDeviceCaps (hPrnDC, HORZRES);
319   int devHeight = GetDeviceCaps (hPrnDC, VERTRES);
320
321   // if context is actually a memory dc, try to retrieve bitmap dimensions
322   // (memory dc could be used for testing purposes)
323   if (GetObjectType (hPrnDC) == OBJ_MEMDC)
324   {
325     // memory dc dimensions
326     BITMAP aBitmapInfo;
327     HBITMAP aMemoryBitmap = (HBITMAP) GetCurrentObject (hPrnDC, OBJ_BITMAP);
328     if (aMemoryBitmap)
329       if (GetObject (aMemoryBitmap, sizeof (BITMAP), &aBitmapInfo))
330       {
331         devWidth  = aBitmapInfo.bmWidth;
332         devHeight = aBitmapInfo.bmHeight;
333       }
334   }
335
336   Standard_Integer tempWidth  = (Standard_Integer) devWidth;
337   Standard_Integer tempHeight = (Standard_Integer) devHeight;
338
339   // view dimensions
340   int viewWidth  = myWidth;
341   int viewHeight = myHeight;
342   if (viewWidth == 0 || viewHeight == 0)
343   {
344     //MessageBox (NULL, "Print failed: can't setup the view for printing.",
345     //            "The operation couldn't be completed.", MB_OK);
346     return Standard_False;
347   }
348
349   // calculate correct width/height ratio
350   Standard_Real viewRatio = (Standard_Real)viewWidth/viewHeight;
351   fitDimensionsRatio(tempWidth, tempHeight, viewRatio);
352
353   // width and height for printing area
354   int width  = (int) (tempWidth  * theScaleFactor);
355   int height = (int) (tempHeight * theScaleFactor);
356
357   // device independent bitmap for the whole view
358 #ifdef HAVE_FREEIMAGE
359   FipHandle  aViewImage  = NULL;
360   BYTE*      aViewBuffer = NULL;
361 #else
362   HDC     hMemDC          = CreateCompatibleDC (hPrnDC);
363   HBITMAP hViewBitmap     = NULL;
364   HGDIOBJ hViewBitmapOld  = NULL;
365   VOID*   aViewBuffer    = NULL;
366 #endif
367
368   // Frame buffer initialization
369   OpenGl_FrameBuffer* aFrameBuffer = NULL;
370   OpenGl_FrameBuffer* aPrevBuffer = (OpenGl_FrameBuffer*) ACView.ptrFBO;
371   Standard_Integer aFrameWidth (0),  aFrameHeight (0),
372                    aPrevBufferX (0), aPrevBufferY (0);
373
374   bool IsTiling = (printAlgorithm == 1);
375
376   // try to use existing frame buffer
377   if (aPrevBuffer)
378   {
379     GLsizei aPrevWidth  = aPrevBuffer->GetSizeX();
380     GLsizei aPrevHeight = aPrevBuffer->GetSizeY();
381     bool isUsable = false;
382
383     // check if its possible to use previous frame buffer
384     if (!IsTiling && aPrevWidth >= width && aPrevHeight >= height)
385     {
386       aFrameWidth  = (Standard_Integer) width;
387       aFrameHeight = (Standard_Integer) height;
388       isUsable = true;
389     }
390     else if (IsTiling)
391     {
392       getDimensionsTiling (aFrameWidth, aFrameHeight, width, height);
393       if (aPrevWidth >= aFrameWidth && aPrevHeight >= aFrameHeight)
394         isUsable = true;
395     }
396
397     // if it is enough memory for image paste dc operation
398     if (isUsable)
399     {
400 #ifdef HAVE_FREEIMAGE
401       // try to allocate fipImage and necessary resources
402       fipImage* anImagePtr = new fipImage (FIT_BITMAP, aFrameWidth,
403                                            aFrameHeight, 24);
404
405       // if allocated succesfully
406       if (anImagePtr->isValid())
407       {
408         aViewImage  = anImagePtr;
409         aViewBuffer = aViewImage->accessPixels ();
410       }
411       else
412         delete anImagePtr;
413
414       if (!aViewBuffer)
415       {
416         isUsable = false;
417         aViewBuffer = NULL;
418         aViewImage  = NULL;
419       }
420 #else
421       // try to allocate compatible bitmap and necessary resources
422       initBitmapBuffer (hMemDC, hViewBitmap,
423                         aFrameWidth, aFrameHeight, aViewBuffer);
424       if (!aViewBuffer)
425       {
426         isUsable = false;
427         if (hViewBitmap)
428           DeleteObject (hViewBitmap);
429         hViewBitmap = NULL;
430       }
431       else
432         hViewBitmapOld = SelectObject (hMemDC, hViewBitmap);
433 #endif
434     }
435
436     // use previous frame buffer
437     if (isUsable)
438     {
439       aPrevBufferX = aPrevWidth;
440       aPrevBufferY = aPrevHeight;
441       aFrameBuffer = aPrevBuffer;
442       aFrameBuffer->ChangeViewport (aFrameWidth, aFrameHeight);
443     }
444   }
445
446   // if previous buffer cannot be used, try to init a new one
447   if (!aFrameBuffer)
448   {
449     aFrameBuffer = new OpenGl_FrameBuffer();
450
451     // try to create the framebuffer with the best possible size
452     Standard_Integer aMaxWidth(0), aMaxHeight(0);
453     getMaxFrameSize (aMaxWidth, aMaxHeight);
454     while (aMaxWidth > 1 && aMaxHeight > 1)
455     {
456       aFrameWidth  = aMaxWidth;
457       aFrameHeight = aMaxHeight;
458
459       // calculate dimensions for different printing algorithms
460       if (!IsTiling)
461         initBufferStretch (aFrameWidth, aFrameHeight, width, height);
462       else
463         initBufferTiling (aFrameWidth, aFrameHeight, width, height);
464
465       // try to initialize framebuffer
466       if (aFrameBuffer->Init (GetGlContext(), aFrameWidth, aFrameHeight))
467       {
468 #ifdef HAVE_FREEIMAGE
469         // try to allocate fipImage and necessary resources
470         fipImage* anImagePtr = new fipImage (FIT_BITMAP, aFrameWidth,
471                                             aFrameHeight, 24);
472
473         // if allocated succesfully
474         if (anImagePtr->isValid())
475         {
476           aViewImage  = anImagePtr;
477           aViewBuffer = aViewImage->accessPixels ();
478         }
479         else
480           delete anImagePtr;
481
482         if (!aViewBuffer)
483         {
484           aFrameBuffer->Release (GetGlContext().operator->());
485           aViewBuffer = NULL;
486           aViewImage  = NULL;
487         }
488         else
489           break;
490 #else
491         // try to allocate compatible bitmap and necessary resources
492         initBitmapBuffer (hMemDC, hViewBitmap,
493                           aFrameWidth, aFrameHeight, aViewBuffer);
494         if (!aViewBuffer)
495         {
496           if (hViewBitmap)
497             DeleteObject (hViewBitmap);
498           aFrameBuffer->Release (GetGlContext().operator->());
499           hViewBitmap = NULL;
500         }
501         else
502         {
503           hViewBitmapOld = SelectObject (hMemDC, hViewBitmap);
504           break;
505         }
506 #endif
507       }
508
509       // not initialized, decrease dimensions
510       aMaxWidth  = aMaxWidth  >> 1;
511       aMaxHeight = aMaxHeight >> 1;
512     }
513
514     // check if there are proper dimensions
515     if (aMaxWidth <= 1 || aMaxHeight <= 1)
516     {
517       MessageBox (NULL, "Print failed: can't allocate buffer for printing.",
518                   "The operation couldn't be completed.", MB_OK);
519
520       if (aFrameBuffer)
521         delete aFrameBuffer;
522 #ifndef HAVE_FREEIMAGE
523       if (hMemDC)
524         DeleteDC (hMemDC);
525 #endif
526
527       return Standard_False;
528     }
529   }
530
531   // setup printing context and viewport
532   myPrintContext = thePrintContext;
533   GLint aViewPortBack[4];
534   GLint anAlignBack = 1;
535   myPrintContext->SetLayerViewport ((GLsizei )aFrameWidth,
536                                     (GLsizei )aFrameHeight);
537   glGetIntegerv (GL_VIEWPORT, aViewPortBack);
538   glGetIntegerv (GL_PACK_ALIGNMENT, &anAlignBack);
539   glPixelStorei (GL_PACK_ALIGNMENT, 4);
540
541   // start document if the printer context is not actually a memory dc
542   // (memory dc could be used for testing purposes)
543   DOCINFO di;
544   if (GetObjectType (hPrnDC) == OBJ_DC)
545   {
546     // Initalize printing procedure
547     di.cbSize = sizeof(DOCINFO);
548     di.lpszDocName = "Open Cascade Document - print v3d view";
549     di.lpszOutput = filename;
550
551     // if can't print the document
552     if (StartDoc (hPrnDC, &di) <= 0 || StartPage (hPrnDC) <= 0)
553     {
554       MessageBox (NULL, "Print failed: printer can't start operation.",
555                   "The operation couldn't be completed.", MB_OK);
556 #ifndef HAVE_FREEIMAGE
557       if (hViewBitmap)
558       {
559         SelectObject (hMemDC, hViewBitmapOld);
560         DeleteObject (hViewBitmap);
561       }
562       DeleteDC (hMemDC);
563 #endif
564
565       myPrintContext.Nullify();
566       return Standard_False;
567     }
568   }
569
570   // activate the offscreen buffer
571   aFrameBuffer->BindBuffer (GetGlContext());
572
573   // calculate offset for centered printing
574   int aDevOffx = (int)(devWidth  - width) /2;
575   int aDevOffy = (int)(devHeight - height)/2;
576
577   // operation complete flag
578   bool isDone = true;
579
580   // Set up status for printing
581   if (!showBackground)
582     NamedStatus |= OPENGL_NS_WHITEBACK;
583
584   if (!IsTiling)
585   {
586     myPrintContext->SetScale ((GLfloat )aFrameWidth /viewWidth,
587                               (GLfloat )aFrameHeight/viewHeight);
588     aFrameBuffer->SetupViewport (GetGlContext());
589     Redraw1(ACView, ACUnderLayer, ACOverLayer, 0);
590     if (!myTransientDrawToFront)
591     {
592       // render to FBO only if allowed to render to back buffer
593       RedrawImmediatMode();
594     }
595     glReadPixels (0, 0, aFrameWidth, aFrameHeight,
596                   GL_BGR_EXT, GL_UNSIGNED_BYTE, (GLvoid* )aViewBuffer);
597
598     // copy result to the printer device and check for errors
599 #ifdef HAVE_FREEIMAGE
600     if (!aViewImage->rescale(width, height, FILTER_BICUBIC) ||
601         !imagePasteDC (hPrnDC, aViewImage, aDevOffx, aDevOffy, width, height))
602       isDone = imageStretchDC (hPrnDC, aViewImage, aDevOffx, aDevOffy,
603                                width, height);
604 #else
605     if (width > aFrameWidth && height > aFrameHeight)
606     {
607       SetStretchBltMode (hPrnDC, STRETCH_HALFTONE);
608       isDone = (StretchBlt (hPrnDC, aDevOffx, aDevOffy, width, height,
609                             hMemDC, 0, 0, aFrameWidth, aFrameHeight, SRCCOPY) != 0); // to avoid warning C4800
610     }
611     else
612     {
613       isDone = (BitBlt (hPrnDC, aDevOffx, aDevOffy, width, height,
614                         hMemDC, 0, 0, SRCCOPY) != 0); // to avoid warning C4800
615     }
616 #endif
617   }
618   else
619   {
620     // calculate total count of frames and cropping size
621     Standard_Integer aPxCropx = 0;
622     Standard_Integer aPxCropy = 0;
623     Standard_Integer aTotalx =
624                      (Standard_Integer)floor ((float)width /aFrameWidth);
625     Standard_Integer aTotaly =
626                      (Standard_Integer)floor ((float)height/aFrameHeight);
627     if (width %aFrameWidth != 0)
628     {
629       aPxCropx = (aFrameWidth - width%aFrameWidth)/2;
630       aTotalx++;
631     }
632     if (height%aFrameHeight != 0)
633     {
634       aPxCropy = (aFrameHeight - height%aFrameHeight)/2;
635       aTotaly++;
636     }
637
638     int anOddPixelx = (width %aFrameWidth) %2;
639     int anOddPixely = (height%aFrameHeight)%2;
640
641     // calculate scale factor for full frames
642     Standard_Real aScalex = (Standard_Real)width /aFrameWidth;
643     Standard_Real aScaley = (Standard_Real)height/aFrameHeight;
644
645     // calculate and set the text scaling factor for printing context
646     GLfloat aScaleRatex = (GLfloat)aFrameWidth /viewWidth;
647     GLfloat aScaleRatey = (GLfloat)aFrameHeight/viewHeight;
648     myPrintContext->SetScale (aScaleRatex * (GLfloat )aScalex,
649                               aScaleRatey * (GLfloat )aScaley);
650
651     // initialize projection matrix for printer context
652     TColStd_Array2OfReal aProj (0, 3, 0, 3);
653     Standard_Real aDef = 0;
654     aProj.Init (aDef);
655     aProj(2,2) = 1.0;
656     aProj(3,3) = 1.0;
657
658     // projection matrix offsets for printer context
659     // offsets are even numbers
660     Standard_Real aOffsetx(0), aOffsety(0);
661     aOffsetx = -(aTotalx-1);
662     aOffsety = -(aTotaly-1);
663
664     // rect of frame image that will be copied
665     // and coordinates in view image where to put it
666     Standard_Integer aLeft = 0, aRight = 0, aBottom = 0, aTop = 0;
667     Standard_Integer aSubLeft = (Standard_Integer)aDevOffx;
668     Standard_Integer aSubTop  = (Standard_Integer)aDevOffy;
669
670     // draw sequence of full frames
671     for (int i = 0; i < aTotalx; i++)
672     {
673       // offsets are even numbers
674       aOffsety = -(aTotaly-1);
675       aSubTop  =  (Standard_Integer)aDevOffy;
676
677       // calculate cropped frame rect
678       aLeft  = (i == 0) ? aPxCropx : 0;
679       aRight = (i == aTotalx-1) ? aFrameWidth-(aPxCropx+anOddPixelx) :
680                                   aFrameWidth;
681
682       for (int j = 0; j < aTotaly; j++)
683       {
684         // no offset for single frames
685         aProj(3,0) = (aTotalx == 1) ? 0 : -aOffsetx;
686         aProj(3,1) = (aTotaly == 1) ? 0 :  aOffsety;
687
688         // set projection matrix
689         aProj(0,0) = aScalex;
690         aProj(1,1) = aScaley;
691         myPrintContext->SetProjTransformation (aProj);
692
693         // calculate cropped frame rect
694         aTop    = (j == 0)         ? aPxCropy : 0;
695         aBottom = (j == aTotaly-1) ? aFrameHeight-(aPxCropy+anOddPixely) :
696                                      aFrameHeight;
697
698         // draw to the offscreen buffer and capture the result
699         aFrameBuffer->SetupViewport (GetGlContext());
700         Redraw1(ACView, ACUnderLayer, ACOverLayer, 0);
701         if (!myTransientDrawToFront)
702         {
703           // render to FBO only if forces to render to back buffer
704           RedrawImmediatMode();
705         }
706         glReadPixels (0, 0, aFrameWidth, aFrameHeight,
707                       GL_BGR_EXT, GL_UNSIGNED_BYTE, (GLvoid* )aViewBuffer);
708 #ifdef HAVE_FREEIMAGE
709         // cut out pixels that are out of printing area
710         isDone = imagePasteDC (hPrnDC, aViewImage, aSubLeft, aSubTop,
711                                aRight-aLeft, aBottom-aTop, aLeft, aTop);
712 #else
713         isDone = (BitBlt (hPrnDC, aSubLeft, aSubTop, aRight-aLeft, aBottom-aTop,
714                           hMemDC, aLeft, aTop, SRCCOPY) != 0); // to avoid warning C4800
715 #endif
716
717         // stop operation if errors
718         if (!isDone)
719           break;
720
721         // calculate new view offset for y-coordinate
722         aOffsety += 2.0;
723         aSubTop  += aBottom-aTop;
724       }
725
726       // stop operation if errors
727       if (!isDone)
728         break;
729
730       // calculate new view offset for x-coordinate
731       aOffsetx += 2.0;
732       aSubLeft += aRight-aLeft;
733     }
734   }
735
736   // complete printing or indicate an error
737   if (GetObjectType (hPrnDC) == OBJ_DC && isDone == true)
738   {
739     EndPage (hPrnDC);
740     EndDoc (hPrnDC);
741   }
742   else if (isDone == false)
743   {
744     MessageBox (NULL, "Print failed: insufficient memory or spool error.\nPlease use smaller printer resolution.",
745                 "The opeartion couldn't be completed.", MB_OK);
746     if (GetObjectType (hPrnDC) == OBJ_DC)
747       AbortDoc (hPrnDC);
748   }
749
750   // return OpenGl to the previous state
751   glPixelStorei (GL_PACK_ALIGNMENT, anAlignBack);
752   aFrameBuffer->UnbindBuffer (GetGlContext());
753   glViewport (aViewPortBack[0], aViewPortBack[1],
754               aViewPortBack[2], aViewPortBack[3]);
755   if (aPrevBuffer)
756   {
757     aPrevBuffer->ChangeViewport (aPrevBufferX, aPrevBufferY);
758   }
759   else
760   {
761     aFrameBuffer->Release (GetGlContext().operator->());
762     delete aFrameBuffer;
763   }
764
765   // delete resources
766 #ifndef HAVE_FREEIMAGE
767   if (hViewBitmap)
768   {
769     SelectObject (hMemDC, hViewBitmapOld);
770     DeleteObject (hViewBitmap);
771   }
772   DeleteDC (hMemDC);
773 #endif
774
775   // Reset status after printing
776   NamedStatus &= ~OPENGL_NS_WHITEBACK;
777
778   myPrintContext.Nullify();
779   return (Standard_Boolean) isDone;
780
781 #else // not _WIN32
782   myPrintContext.Nullify();
783   return Standard_False;
784 #endif
785 }
786
787 /*----------------------------------------------------------------------*/
788
789 //redrawView
790 void OpenGl_Workspace::Redraw1 (const Graphic3d_CView& ACView,
791                                 const Aspect_CLayer2d& ACUnderLayer,
792                                 const Aspect_CLayer2d& ACOverLayer,
793                                 const int aswap)
794 {
795   if (myDisplay.IsNull() || myView.IsNull())
796     return;
797
798   // Request reset of material
799   NamedStatus |= OPENGL_NS_RESMAT;
800
801   /* GL_DITHER on/off pour le background */
802   if (myBackDither)
803     glEnable (GL_DITHER);
804   else
805     glDisable (GL_DITHER);
806
807   GLbitfield toClear = GL_COLOR_BUFFER_BIT;
808   if ( myUseZBuffer )
809   {
810     glDepthFunc(GL_LEQUAL);
811     glDepthMask(GL_TRUE);
812
813     // SAV checking if depth test was deprecated somewhere outside
814     if ( myUseDepthTest )
815       glEnable(GL_DEPTH_TEST);
816     else
817       glDisable(GL_DEPTH_TEST);
818
819     glClearDepth(1.0);
820     toClear |= GL_DEPTH_BUFFER_BIT;
821   }
822   else
823   {
824     glDisable(GL_DEPTH_TEST);
825   }
826
827   if ( NamedStatus & OPENGL_NS_WHITEBACK )
828   {
829     // Set background to white
830     glClearColor (1.F, 1.F, 1.F, 1.F);
831     toClear |= GL_DEPTH_BUFFER_BIT;
832   }
833   else
834   {
835     glClearColor (myBgColor.rgb[0], myBgColor.rgb[1], myBgColor.rgb[2], 0.F);
836   }
837   glClear (toClear);
838
839   Handle(OpenGl_Workspace) aWS(this);
840   myView->Render (myPrintContext, aWS, ACView, ACUnderLayer, ACOverLayer);
841
842   // Swap the buffers
843   if ( aswap )
844   {
845     GetGlContext()->SwapBuffers();
846     myBackBufferRestored = Standard_False;
847   }
848   else
849     glFlush();
850 }
851
852 /*----------------------------------------------------------------------*/
853
854 //TelCopyBuffers
855 void OpenGl_Workspace::CopyBuffers (const Standard_Boolean theFrontToBack)
856 {
857   if (theFrontToBack)
858   {
859     myBackBufferRestored = Standard_False;
860   }
861
862   glMatrixMode (GL_PROJECTION);
863   glPushMatrix ();
864   glLoadIdentity ();
865   gluOrtho2D ((GLdouble) 0., (GLdouble) myWidth, 0., (GLdouble) myHeight);
866   glMatrixMode (GL_MODELVIEW);
867   glPushMatrix ();
868   glLoadIdentity ();
869
870   DisableFeatures();
871
872   glDrawBuffer (theFrontToBack ? GL_BACK  : GL_FRONT);
873   glReadBuffer (theFrontToBack ? GL_FRONT : GL_BACK);
874
875   glRasterPos2i (0, 0);
876   glCopyPixels  (0, 0, myWidth  + 1, myHeight + 1, GL_COLOR);
877
878   EnableFeatures();
879
880   glMatrixMode (GL_PROJECTION);
881   glPopMatrix ();
882   glMatrixMode (GL_MODELVIEW);
883   glPopMatrix ();
884
885   glDrawBuffer (GL_BACK);
886 }
887
888 /*----------------------------------------------------------------------*/
889
890 //call_subr_displayCB
891 void OpenGl_Workspace::DisplayCallback (const Graphic3d_CView& theCView,
892                                         int theReason)
893 {
894   if (theCView.GDisplayCB == NULL)
895   {
896     return;
897   }
898
899   Aspect_GraphicCallbackStruct aCallData;
900   aCallData.reason    = theReason;
901   aCallData.glContext = GetGlContext();
902   aCallData.wsID      = theCView.WsId;
903   aCallData.viewID    = theCView.ViewId;
904   theCView.GDisplayCB (theCView.DefWindow.XWindow, theCView.GClientData, &aCallData);
905 }
906
907 /*----------------------------------------------------------------------*/