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