45e26e070bcc79589c75cf11bec64d47b7a0ddf6
[occt.git] / src / OpenGl / OpenGl_Workspace_2.cxx
1 // File:      OpenGl_Workspace_2.cxx
2 // Created:   20 September 2011
3 // Author:    Sergey ZERCHANINOV
4 // Copyright: OPEN CASCADE 2011
5
6 #define RIC120302       /* GG Enable to use the application display
7 //                      callback at end of traversal
8 */
9
10 #include <OpenGl_tgl_all.hxx>
11 #include <OpenGl_FrameBuffer.hxx>
12 #include <TColStd_Array2OfReal.hxx>
13 #include <string.h>
14 #include <OpenGl_telem_util.hxx>
15
16 #ifdef HAVE_FREEIMAGE
17   #include <NCollection_Handle.hxx>
18   #include <FreeImagePlus.h>
19   #ifdef _MSC_VER
20   #pragma comment( lib, "FreeImage.lib" )
21   #pragma comment( lib, "FreeImagePlus.lib" )
22   #endif
23   typedef NCollection_Handle<fipImage> FipHandle;
24 #endif
25
26 #include <OpenGl_PrinterContext.hxx>
27 #include <OpenGl_Workspace.hxx>
28 #include <OpenGl_View.hxx>
29 #include <OpenGl_Display.hxx>
30
31 //10-05-96 : CAL ; Ajout d'un nouveau delta dans les copies de pixels (voir CALL_DEF_DELTA)
32 #define CALL_DEF_DELTA 10
33
34 // ---------------------------------------------------------------
35 // Function: getNearestPowOfTwo
36 // Purpose:  get the nearest power of two for theNumber
37 // ---------------------------------------------------------------
38 static GLsizei getNearestPowOfTwo (const GLsizei theNumber)
39 {
40   GLsizei aLast = 1;
41   for (GLsizei p2 = 1; p2 <= theNumber; aLast = p2, p2 <<= 1);
42   return aLast;
43 }
44
45 // ---------------------------------------------------------------
46 // Function: getMaxFrameSize
47 // Purpose:  get the maximum possible frame size
48 // ---------------------------------------------------------------
49 static void getMaxFrameSize(Standard_Integer& theWidth,
50                             Standard_Integer& theHeight)
51 {
52   GLsizei aMaxX, aMaxY;
53   GLint aVpDim[2];
54   GLint aTexDim = 2048;
55   glGetIntegerv (GL_MAX_VIEWPORT_DIMS, (GLint*) &aVpDim);
56   glGetIntegerv (GL_MAX_TEXTURE_SIZE, &aTexDim);
57   (aVpDim[0] >= aTexDim) ? aMaxX = (GLsizei) aTexDim : 
58                            aMaxX = getNearestPowOfTwo((GLsizei)aVpDim[0]);
59   (aVpDim[1] >= aTexDim) ? aMaxY = (GLsizei) aTexDim :
60                            aMaxY = getNearestPowOfTwo((GLsizei)aVpDim[1]);
61
62   theWidth  = (Standard_Integer)aMaxX;
63   theHeight = (Standard_Integer)aMaxY;
64 }
65
66 // ---------------------------------------------------------------
67 // Function: fitDimensionsRatio
68 // Purpose:  calculate correct width/height ratio for theWidth and
69 //           theHeight parameters
70 // ---------------------------------------------------------------
71 static void fitDimensionsRatio (Standard_Integer& theWidth,
72                                 Standard_Integer& theHeight,
73                                 const Standard_Real theViewRatio)
74 {
75   // set dimensions in accordance with the viewratio
76   if (theHeight <  theWidth/theViewRatio)
77       theWidth  = (Standard_Integer)(theHeight*theViewRatio);
78
79   if (theWidth  <  theHeight*theViewRatio)
80       theHeight = (Standard_Integer)(theWidth/theViewRatio);
81 }
82
83 // ---------------------------------------------------------------
84 // Function: getDimensionsTiling
85 // Purpose:  calculate maximum possible dimensions for framebuffer 
86 //           in tiling mode according to the view size
87 // ---------------------------------------------------------------
88 static void getDimensionsTiling (Standard_Integer& theFrameWidth,
89                                  Standard_Integer& theFrameHeight,
90                                  const int theViewWidth,
91                                  const int theViewHeight)
92 {
93   // fit the maximum dimensions into the printing area
94   if (theFrameWidth > theViewWidth)
95       theFrameWidth = theViewWidth;
96
97   if (theFrameHeight > theViewHeight)
98       theFrameHeight = theViewHeight;
99 }
100
101 // ---------------------------------------------------------------
102 // Function: initBufferStretch
103 // Purpose:  calculate initialization sizes for frame buffer
104 //           when the stretch algorithm is selected
105 // ---------------------------------------------------------------
106 static void initBufferStretch (Standard_Integer& theFrameWidth,
107                                Standard_Integer& theFrameHeight,
108                                const int theViewWidth,
109                                const int theViewHeight)
110 {
111
112   // Calculate correct width/height for framebuffer
113   Standard_Real aViewRatio = (Standard_Real)theViewWidth/theViewHeight;
114   fitDimensionsRatio (theFrameWidth, theFrameHeight, aViewRatio);
115
116   // downscale the framebuffer if it is too large
117   Standard_Real aWidthRate  = (Standard_Real)theFrameWidth /theViewWidth;
118   Standard_Real aHeightRate = (Standard_Real)theFrameHeight/theViewHeight;
119
120   if ((aWidthRate > 1 && aHeightRate > 1 && aWidthRate >= aHeightRate) || 
121       (aWidthRate > 1 && aHeightRate <= 1))
122   {
123     theFrameWidth  = (Standard_Integer)(theFrameWidth /aWidthRate);
124     theFrameHeight = (Standard_Integer)(theFrameHeight/aWidthRate);
125   }
126   else if ((aWidthRate  > 1 && aHeightRate > 1 && aWidthRate < aHeightRate) ||
127            (aWidthRate <= 1 && aHeightRate > 1))
128   {
129     theFrameWidth  = (Standard_Integer)(theFrameWidth /aHeightRate);
130     theFrameHeight = (Standard_Integer)(theFrameHeight/aHeightRate);
131   }
132
133 }
134
135 // ---------------------------------------------------------------
136 // Function: initBufferTiling
137 // Purpose:  calculate initialization sizes for frame buffer
138 //           when the tile algorithm is selected
139 // ---------------------------------------------------------------
140 static void initBufferTiling (Standard_Integer& theFrameWidth,
141                               Standard_Integer &theFrameHeight,
142                               const int theViewWidth,
143                               const int theViewHeight)
144 {
145   // fit framebuffer into the printing area
146   if (theFrameWidth > theViewWidth)
147       theFrameWidth = theViewWidth;
148
149   if (theFrameHeight > theViewHeight)
150       theFrameHeight = theViewHeight;
151 }
152
153 // ---------------------------------------------------------------
154 // Function: initBitmapBuffer
155 // Purpose:  init device independent bitmap to hold printing data
156 // ---------------------------------------------------------------
157 #ifdef WNT
158 #ifndef HAVE_FREEIMAGE
159 static void initBitmapBuffer (const HDC theMemoryDC,
160                               HBITMAP &theMemoryBmp,
161                               const   Standard_Integer theBmpWidth,
162                               const   Standard_Integer theBmpHeight,
163                               VOID*   &theBufferPtr)
164 {
165   // define compatible bitmap
166   BITMAPINFO aBitmapData;
167   memset (&aBitmapData, 0, sizeof (BITMAPINFOHEADER));
168   aBitmapData.bmiHeader.biSize          = sizeof (BITMAPINFOHEADER);
169   aBitmapData.bmiHeader.biWidth         = theBmpWidth;
170   aBitmapData.bmiHeader.biHeight        = theBmpHeight;
171   aBitmapData.bmiHeader.biPlanes        = 1;
172   aBitmapData.bmiHeader.biBitCount      = 24;
173   aBitmapData.bmiHeader.biXPelsPerMeter = 0;
174   aBitmapData.bmiHeader.biYPelsPerMeter = 0;
175   aBitmapData.bmiHeader.biClrUsed       = 0;
176   aBitmapData.bmiHeader.biClrImportant  = 0;
177   aBitmapData.bmiHeader.biCompression   = BI_RGB;
178   aBitmapData.bmiHeader.biSizeImage     = 0;
179
180   // Create Device Independent Bitmap
181   theMemoryBmp = CreateDIBSection (theMemoryDC, &aBitmapData, DIB_RGB_COLORS,
182                                    &theBufferPtr, NULL, 0);
183 }
184 #else
185 // ---------------------------------------------------------------
186 // Function: imagePasteDC
187 // Purpose:  copy the data from image buffer to the device context
188 // ---------------------------------------------------------------
189 static bool imagePasteDC(HDC theDstDC,    FipHandle theImage, int theOffsetX,
190                          int theOffsetY,  int theWidth, int theHeight, 
191                          int theLeft = 0, int theTop = 0)
192 {
193   // get image parameters
194   BITMAPINFO* aBitmapData = theImage->getInfo ();
195   SetStretchBltMode (theDstDC, STRETCH_HALFTONE);
196  
197   // organize blocks data passing if memory isn't enough to pass all the data
198   // at once
199   int aLinesComplete = 0, aMaxBlockWidth = theHeight, aBlockWidth = 0,
200       aPassed        = 0, aInverseLine   = 0, aScan = 0;
201   BYTE *aDataPtr = 0;
202   while (aMaxBlockWidth >= 1 && aLinesComplete < theHeight)
203   {
204     // how much lines still to pass
205     aBlockWidth = theHeight - aLinesComplete;
206
207     // normalize count of lines to pass to maximum lines count at one pass.
208     if (aBlockWidth > aMaxBlockWidth)
209       aBlockWidth = aMaxBlockWidth;
210
211     // access image data at the start scan line, we need to calculate scan from
212     // the bottom of image (image is bottom-left, the src coord is top-left)
213     aInverseLine = theTop + aBlockWidth + aLinesComplete;
214     aScan = theImage->getHeight() - aInverseLine;
215     aDataPtr = theImage->getScanLine (aScan);
216     if (!aDataPtr)
217       return false;
218
219     // try to pass block to the device
220     if (aBlockWidth > 0)
221     {
222       // instead of banded output we provide blocked as it isn't always passed
223       // to printer as it is expected
224       aPassed = SetDIBitsToDevice (theDstDC, theOffsetX,
225                                    theOffsetY + aLinesComplete,
226                                    theWidth, aBlockWidth, theLeft, 0,
227                                    0, aBlockWidth,
228                                    aDataPtr, aBitmapData, DIB_RGB_COLORS);
229
230       // if result is bad, try to decrease band width
231       if (aPassed != aBlockWidth)
232       {
233         aMaxBlockWidth = aMaxBlockWidth >> 1;
234         aLinesComplete = 0;
235       }
236       else
237         aLinesComplete += aBlockWidth;
238     }
239   }
240
241   // check for total failure
242   if (aMaxBlockWidth < 1)
243     return false;
244
245   return true;
246 }
247
248 // ---------------------------------------------------------------
249 // Function: imageStretchDC
250 // Purpose:  copy pixels from image to dc by stretching them
251 // ---------------------------------------------------------------
252 static bool imageStretchDC(HDC theDstDC,   FipHandle theImage, int theOffsetX,
253                            int theOffsetY, int theWidth, int theHeight)
254 {
255   // access to raw image data
256   BYTE *aDataPtr = theImage->accessPixels ();
257   if (!aDataPtr)
258     return false;
259
260   // get image parameters
261   unsigned int widthPx    = theImage->getWidth ();
262   unsigned int heightPx   = theImage->getHeight ();
263   BITMAPINFO* aBitmapData = theImage->getInfo ();
264   SetStretchBltMode (theDstDC, STRETCH_HALFTONE);
265   
266   // pass lines and check if operation is succesfull
267   int aPassed = 0;
268   aPassed = StretchDIBits (theDstDC, theOffsetX, theOffsetY, theWidth,
269                            theHeight, 0, 0, widthPx, heightPx, aDataPtr,
270                            aBitmapData, DIB_RGB_COLORS, SRCCOPY);
271
272   if (aPassed != heightPx)
273     return false;
274  
275   return true;
276 }
277 #endif
278 #endif
279
280 // ---------------------------------------------------------------
281 // ---------------------------------------------------------------
282
283 //call_togl_print
284 Standard_Boolean OpenGl_Workspace::Print
285   (const Graphic3d_CView& ACView, 
286    const Aspect_CLayer2d& ACUnderLayer, 
287    const Aspect_CLayer2d& ACOverLayer,
288    const Aspect_Handle    hPrintDC,// const Aspect_Drawable hPrintDC,
289    const Standard_Boolean showBackground,
290    const Standard_CString filename,
291    const Aspect_PrintAlgo printAlgorithm,
292    const Standard_Real theScaleFactor)
293 {
294 #ifdef WNT
295
296   if (!Activate())
297   {
298     //MessageBox (NULL, "Print failed: can't setup the view for printing.",
299     //            "The operation couldn't be completed.", MB_OK);
300     return Standard_False;
301   }
302
303   // printer page dimensions
304   HDC hPrnDC = (HDC) hPrintDC;
305   int devWidth  = GetDeviceCaps (hPrnDC, HORZRES);
306   int devHeight = GetDeviceCaps (hPrnDC, VERTRES);
307
308   // if context is actually a memory dc, try to retrieve bitmap dimensions
309   // (memory dc could be used for testing purposes)
310   if (GetObjectType (hPrnDC) == OBJ_MEMDC)
311   {
312     // memory dc dimensions
313     BITMAP aBitmapInfo;
314     HBITMAP aMemoryBitmap = (HBITMAP) GetCurrentObject (hPrnDC, OBJ_BITMAP);
315     if (aMemoryBitmap)
316       if (GetObject (aMemoryBitmap, sizeof (BITMAP), &aBitmapInfo))
317       {
318         devWidth  = aBitmapInfo.bmWidth;
319         devHeight = aBitmapInfo.bmHeight;
320       }
321   }
322
323   Standard_Integer tempWidth  = (Standard_Integer) devWidth;
324   Standard_Integer tempHeight = (Standard_Integer) devHeight;
325
326   // view dimensions
327   int viewWidth  = myWidth;
328   int viewHeight = myHeight;
329   if (viewWidth == 0 || viewHeight == 0)
330   {
331     //MessageBox (NULL, "Print failed: can't setup the view for printing.",
332     //            "The operation couldn't be completed.", MB_OK);
333     return Standard_False;
334   }
335
336   // calculate correct width/height ratio
337   Standard_Real viewRatio = (Standard_Real)viewWidth/viewHeight;
338   fitDimensionsRatio(tempWidth, tempHeight, viewRatio);
339
340   // width and height for printing area
341   int width  = (int) (tempWidth  * theScaleFactor);
342   int height = (int) (tempHeight * theScaleFactor);
343
344   // device independent bitmap for the whole view
345 #ifdef HAVE_FREEIMAGE
346   FipHandle  aViewImage  = NULL;
347   BYTE*      aViewBuffer = NULL;
348 #else
349   HDC     hMemDC          = CreateCompatibleDC (hPrnDC);
350   HBITMAP hViewBitmap     = NULL;
351   HGDIOBJ hViewBitmapOld  = NULL;
352   VOID*   aViewBuffer    = NULL;
353 #endif
354
355   // Frame buffer initialization
356   OpenGl_FrameBuffer* aFrameBuffer = NULL;
357   OpenGl_FrameBuffer* aPrevBuffer = (OpenGl_FrameBuffer*) ACView.ptrFBO;
358   Standard_Integer aFrameWidth (0),  aFrameHeight (0),
359                    aPrevBufferX (0), aPrevBufferY (0);
360
361   bool IsTiling = (printAlgorithm == 1);
362
363   // try to use existing frame buffer
364   if (aPrevBuffer)
365   {
366     GLsizei aPrevWidth  = aPrevBuffer->GetSizeX();
367     GLsizei aPrevHeight = aPrevBuffer->GetSizeY();
368     bool isUsable = false;
369
370     // check if its possible to use previous frame buffer
371     if (!IsTiling && aPrevWidth >= width && aPrevHeight >= height)
372     {
373       aFrameWidth  = (Standard_Integer) width;
374       aFrameHeight = (Standard_Integer) height;
375       isUsable = true;
376     }
377     else if (IsTiling)
378     {
379       getDimensionsTiling (aFrameWidth, aFrameHeight, width, height);
380       if (aPrevWidth >= aFrameWidth && aPrevHeight >= aFrameHeight)
381         isUsable = true;
382     }
383
384     // if it is enough memory for image paste dc operation
385     if (isUsable)
386     {
387 #ifdef HAVE_FREEIMAGE
388       // try to allocate fipImage and necessary resources
389       fipImage* anImagePtr = new fipImage (FIT_BITMAP, aFrameWidth,
390                                            aFrameHeight, 24);
391
392       // if allocated succesfully
393       if (anImagePtr->isValid())
394       {
395         aViewImage  = anImagePtr;
396         aViewBuffer = aViewImage->accessPixels ();
397       }
398       else
399         delete anImagePtr;
400
401       if (!aViewBuffer)
402       {
403         isUsable = false;
404         aViewBuffer = NULL;
405         aViewImage  = NULL;
406       }
407 #else
408       // try to allocate compatible bitmap and necessary resources
409       initBitmapBuffer (hMemDC, hViewBitmap, 
410                         aFrameWidth, aFrameHeight, aViewBuffer);
411       if (!aViewBuffer)
412       {
413         isUsable = false;
414         if (hViewBitmap)
415           DeleteObject (hViewBitmap);
416         hViewBitmap = NULL;
417       }
418       else
419         hViewBitmapOld = SelectObject (hMemDC, hViewBitmap);
420 #endif
421     }
422
423     // use previous frame buffer
424     if (isUsable)
425     {
426       aPrevBufferX = aPrevWidth;
427       aPrevBufferY = aPrevHeight;
428       aFrameBuffer = aPrevBuffer;
429       aFrameBuffer->ChangeViewport (aFrameWidth, aFrameHeight);
430     }
431   }
432
433   // if previous buffer cannot be used, try to init a new one
434   if (!aFrameBuffer)
435   {
436     aFrameBuffer = new OpenGl_FrameBuffer();
437
438     // try to create the framebuffer with the best possible size
439     Standard_Integer aMaxWidth(0), aMaxHeight(0);
440     getMaxFrameSize (aMaxWidth, aMaxHeight);
441     while (aMaxWidth > 1 && aMaxHeight > 1)
442     {
443       aFrameWidth  = aMaxWidth;
444       aFrameHeight = aMaxHeight;
445
446       // calculate dimensions for different printing algorithms
447       if (!IsTiling)
448         initBufferStretch (aFrameWidth, aFrameHeight, width, height);
449       else
450         initBufferTiling (aFrameWidth, aFrameHeight, width, height);
451
452       // try to initialize framebuffer
453       if (aFrameBuffer->Init (GetGlContext(), aFrameWidth, aFrameHeight))
454       {
455 #ifdef HAVE_FREEIMAGE
456         // try to allocate fipImage and necessary resources
457         fipImage* anImagePtr = new fipImage (FIT_BITMAP, aFrameWidth,
458                                             aFrameHeight, 24);
459
460         // if allocated succesfully
461         if (anImagePtr->isValid())
462         {
463           aViewImage  = anImagePtr;
464           aViewBuffer = aViewImage->accessPixels ();
465         }
466         else
467           delete anImagePtr;
468
469         if (!aViewBuffer)
470         {
471           aFrameBuffer->Release (GetGlContext());
472           aViewBuffer = NULL;
473           aViewImage  = NULL;
474         }
475         else
476           break;
477 #else
478         // try to allocate compatible bitmap and necessary resources
479         initBitmapBuffer (hMemDC, hViewBitmap, 
480                           aFrameWidth, aFrameHeight, aViewBuffer);
481         if (!aViewBuffer)
482         {
483           if (hViewBitmap)
484             DeleteObject (hViewBitmap);
485           aFrameBuffer->Release ();
486           hViewBitmap = NULL;
487         }
488         else
489         {
490           hViewBitmapOld = SelectObject (hMemDC, hViewBitmap);
491           break;
492         }
493 #endif
494       }
495
496       // not initialized, decrease dimensions
497       aMaxWidth  = aMaxWidth  >> 1;
498       aMaxHeight = aMaxHeight >> 1;
499     }
500
501     // check if there are proper dimensions 
502     if (aMaxWidth <= 1 || aMaxHeight <= 1)
503     {
504       MessageBox (NULL, "Print failed: can't allocate buffer for printing.",
505                   "The operation couldn't be completed.", MB_OK);
506
507       if (aFrameBuffer)
508         delete aFrameBuffer;
509 #ifndef HAVE_FREEIMAGE
510       if (hMemDC)
511         DeleteDC (hMemDC);
512 #endif
513
514       return Standard_False;
515     }
516   }
517
518   // setup printing context and viewport
519   GLint aViewPortBack[4]; 
520   GLint aReadBufferPrev = GL_BACK;
521   GLint anAlignBack     = 1;
522
523   OpenGl_PrinterContext aPrinterContext (myGContext);
524   aPrinterContext.SetLayerViewport ((GLsizei)aFrameWidth,
525                                     (GLsizei)aFrameHeight);
526   glGetIntegerv (GL_VIEWPORT, aViewPortBack);
527   glGetIntegerv (GL_PACK_ALIGNMENT, &anAlignBack);
528   glPixelStorei (GL_PACK_ALIGNMENT, 4);
529
530   // start document if the printer context is not actually a memory dc
531   // (memory dc could be used for testing purposes)
532   DOCINFO di;
533   if (GetObjectType (hPrnDC) == OBJ_DC)
534   {
535     // Initalize printing procedure
536     di.cbSize = sizeof(DOCINFO);
537     di.lpszDocName = "Open Cascade Document - print v3d view";
538     di.lpszOutput = filename;
539
540     // if can't print the document
541     if (StartDoc (hPrnDC, &di) <= 0 || StartPage (hPrnDC) <= 0)
542     {
543       MessageBox (NULL, "Print failed: printer can't start operation.",
544                   "The operation couldn't be completed.", MB_OK);
545 #ifndef HAVE_FREEIMAGE
546       if (hViewBitmap)
547       {
548         SelectObject (hMemDC, hViewBitmapOld);
549         DeleteObject (hViewBitmap);
550       }
551       DeleteDC (hMemDC);
552 #endif
553
554       return Standard_False;
555     }
556   }
557
558   // activate the offscreen buffer
559   aFrameBuffer->BindBuffer (GetGlContext());
560
561   // calculate offset for centered printing
562   int aDevOffx = (int)(devWidth  - width) /2;
563   int aDevOffy = (int)(devHeight - height)/2;
564
565   // operation complete flag
566   bool isDone = true;
567
568   // Set up status for printing
569   NamedStatus |= OPENGL_NS_ISBITMAP;
570   if (!showBackground)
571     NamedStatus |= OPENGL_NS_WHITEBACK;
572
573   if (!IsTiling)
574   {
575     aPrinterContext.SetScale ((GLfloat)aFrameWidth /viewWidth,
576                               (GLfloat)aFrameHeight/viewHeight);
577     aFrameBuffer->SetupViewport ();
578     Redraw1(ACView, ACUnderLayer, ACOverLayer, 0);
579     RedrawImmediatMode();
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);
595     }
596     else
597     {
598       isDone = BitBlt (hPrnDC, aDevOffx, aDevOffy, width, height,
599                        hMemDC, 0, 0, SRCCOPY);
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     aPrinterContext.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         aPrinterContext.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 ();
685         Redraw1(ACView, ACUnderLayer, ACOverLayer, 0);
686         RedrawImmediatMode();
687         glReadPixels (0, 0, aFrameWidth, aFrameHeight,
688                       GL_BGR_EXT, GL_UNSIGNED_BYTE, (GLvoid* )aViewBuffer);
689 #ifdef HAVE_FREEIMAGE
690         // cut out pixels that are out of printing area
691         isDone = imagePasteDC (hPrnDC, aViewImage, aSubLeft, aSubTop,
692                                aRight-aLeft, aBottom-aTop, aLeft, aTop);
693 #else
694         isDone = BitBlt (hPrnDC, aSubLeft, aSubTop, aRight-aLeft, aBottom-aTop,
695                          hMemDC, aLeft, aTop, SRCCOPY);
696 #endif
697
698         // stop operation if errors
699         if (!isDone)
700           break;
701
702         // calculate new view offset for y-coordinate
703         aOffsety += 2.0;
704         aSubTop  += aBottom-aTop;
705       }
706
707       // stop operation if errors
708       if (!isDone)
709         break;
710  
711       // calculate new view offset for x-coordinate
712       aOffsetx += 2.0;
713       aSubLeft += aRight-aLeft;
714     }
715   }
716
717   // complete printing or indicate an error
718   if (GetObjectType (hPrnDC) == OBJ_DC && isDone == true)
719   {
720     EndPage (hPrnDC);
721     EndDoc (hPrnDC);
722   }
723   else if (isDone == false)
724   {
725     MessageBox (NULL, "Print failed: insufficient memory or spool error.\nPlease use smaller printer resolution.",
726                 "The opeartion couldn't be completed.", MB_OK);
727     if (GetObjectType (hPrnDC) == OBJ_DC)
728       AbortDoc (hPrnDC);
729   }
730   
731   // return OpenGl to the previous state
732   aPrinterContext.Deactivate ();
733   glPixelStorei (GL_PACK_ALIGNMENT, anAlignBack);
734   aFrameBuffer->UnbindBuffer (GetGlContext());
735   glViewport (aViewPortBack[0], aViewPortBack[1], 
736               aViewPortBack[2], aViewPortBack[3]);
737   if (aPrevBuffer)
738     aPrevBuffer->ChangeViewport (aPrevBufferX, aPrevBufferY);
739   else
740     delete aFrameBuffer;
741
742   // delete resources
743 #ifndef HAVE_FREEIMAGE
744   if (hViewBitmap)
745   {
746     SelectObject (hMemDC, hViewBitmapOld);
747     DeleteObject (hViewBitmap);
748   }
749   DeleteDC (hMemDC);
750 #endif
751
752   // Reset status after printing
753   NamedStatus &= ~(OPENGL_NS_WHITEBACK | OPENGL_NS_ISBITMAP);
754
755   return (Standard_Boolean) isDone;
756
757 #else // not WNT
758   return Standard_False;
759 #endif 
760 }
761
762 /*----------------------------------------------------------------------*/
763
764 //redrawView
765 void OpenGl_Workspace::Redraw1 (const Graphic3d_CView& ACView, 
766                                const Aspect_CLayer2d& ACUnderLayer, 
767                                const Aspect_CLayer2d& ACOverLayer,
768                                const int aswap)
769 {
770   if (myDisplay.IsNull() || myView.IsNull())
771     return;
772
773   myDisplay->UpdateUserMarkers();
774
775   // Request reset of material
776   NamedStatus |= OPENGL_NS_RESMAT;
777
778   /* GL_DITHER on/off pour le background */
779   if (myBackDither)
780     glEnable (GL_DITHER);
781   else
782     glDisable (GL_DITHER);
783
784   GLbitfield toClear = GL_COLOR_BUFFER_BIT;
785   if ( myUseZBuffer )
786   {
787     glDepthFunc(GL_LEQUAL);
788     glDepthMask(GL_TRUE);
789
790     // SAV checking if depth test was deprecated somewhere outside
791     if ( myUseDepthTest )
792       glEnable(GL_DEPTH_TEST);
793     else
794       glDisable(GL_DEPTH_TEST);
795
796     glClearDepth(1.0);
797         toClear |= GL_DEPTH_BUFFER_BIT;
798   }
799   else
800   {
801     glDisable(GL_DEPTH_TEST);
802   }
803
804   if ( NamedStatus & OPENGL_NS_WHITEBACK )
805   {
806     // Set background to white
807     glClearColor (1.F, 1.F, 1.F, 1.F);
808         toClear |= GL_DEPTH_BUFFER_BIT;
809   }
810   else
811   {
812     glClearColor (myBgColor.rgb[0], myBgColor.rgb[1], myBgColor.rgb[2], 0.F);
813   }
814   glClear (toClear);
815
816   Handle(OpenGl_Workspace) aWS(this);
817   myView->Render(aWS,ACView,ACUnderLayer,ACOverLayer);
818
819   // Swap the buffers
820   if ( aswap )
821   {
822 #ifndef WNT
823     glXSwapBuffers ((Display*)myDisplay->GetDisplay (), myWindow );
824 #else
825     SwapBuffers ( wglGetCurrentDC () );
826     glFlush();
827 #endif  /* WNT */
828     myBackBufferRestored = Standard_False;
829   }
830   else
831     glFlush();
832
833   myIsUpdated = Standard_True;
834 }
835
836 /*----------------------------------------------------------------------*/
837
838 //TelCopyBuffers
839 void OpenGl_Workspace::CopyBuffers (Tint vid, int FrontToBack, Tfloat xm, Tfloat ym, Tfloat zm, Tfloat XM, Tfloat YM, Tfloat ZM, Tint flag)
840 {
841   if (FrontToBack) myBackBufferRestored = Standard_False;
842
843   glMatrixMode (GL_PROJECTION);
844   glPushMatrix ();
845   glLoadIdentity ();
846   gluOrtho2D ((GLdouble) 0., (GLdouble) myWidth, 0., (GLdouble) myHeight);
847   glMatrixMode (GL_MODELVIEW);
848   glPushMatrix ();
849   glLoadIdentity ();
850
851   DisableFeatures();
852
853   GLsizei width = myWidth+1, height = myHeight+1;
854   Tfloat xmr = 0, ymr = 0;
855
856   if (flag) 
857   {
858     if (!myView.IsNull()) //szvgl: use vid here!
859         {
860     // Calculate bounding box and store the projected rectangle
861     Tfloat xr[8], yr[8];
862     // Project bounding box
863     if (myView->ProjectObjectToRaster (myWidth, myHeight, xm, ym, zm, xr[0], yr[0]) &&
864         myView->ProjectObjectToRaster (myWidth, myHeight, xm, YM, zm, xr[1], yr[1]) &&
865         myView->ProjectObjectToRaster (myWidth, myHeight, XM, YM, zm, xr[2], yr[2]) &&
866         myView->ProjectObjectToRaster (myWidth, myHeight, XM, ym, zm, xr[3], yr[3]) &&
867         myView->ProjectObjectToRaster (myWidth, myHeight, xm, ym, ZM, xr[4], yr[4]) &&
868         myView->ProjectObjectToRaster (myWidth, myHeight, xm, YM, ZM, xr[5], yr[5]) &&
869         myView->ProjectObjectToRaster (myWidth, myHeight, XM, YM, ZM, xr[6], yr[6]) &&
870         myView->ProjectObjectToRaster (myWidth, myHeight, XM, ym, ZM, xr[7], yr[7]))
871     {
872       Tfloat XMR, YMR;
873       xmr = ymr = (float ) shortreallast ();
874       XMR = YMR = (float ) shortrealfirst ();
875       /*
876       * Recherche du rectangle projete
877       */
878       Tint i;
879       for (i=0; i<8; i++) {
880         if (xmr > xr[i]) xmr = xr[i];
881         if (ymr > yr[i]) ymr = yr[i];
882         if (XMR < xr[i]) XMR = xr[i];
883         if (YMR < yr[i]) YMR = yr[i];
884       }
885       /* pour eviter les bavures de pixels ! */
886       xmr--;ymr--;
887       XMR++;YMR++;
888
889       /*
890       * Ajout CAL : 10/05/96
891       * Si les MinMax viennent d'un ensemble de markers
892       * on ne tient pas compte du scale factor de ceux-ci
893       * dans les valeurs de MinMax. En effet, ce facteur
894       * est dans l'espace pixel et les MinMax dans l'espace
895       * du modele. Donc ajout d'un delta de pixels
896       * en esperant que les applis n'utilisent pas des
897       * markers tres gros !
898       */
899       xmr -= CALL_DEF_DELTA; ymr -= CALL_DEF_DELTA;
900       XMR += CALL_DEF_DELTA; YMR += CALL_DEF_DELTA;
901
902       /*
903       * Le rectangle projete peut-etre clippe
904       */
905       width = (GLsizei) (XMR-xmr+1);
906       height = (GLsizei) (YMR-ymr+1);
907       /*
908       * (xmr,ymr) coin inferieur gauche
909       * (XMR,YMR) coin superieur droit
910       */
911       /* cas ou 1 coin est en dehors de la fenetre */
912       if (xmr < 0) { width  = (GLsizei) (XMR+1); xmr = 0; }
913       if (ymr < 0) { height = (GLsizei) (YMR+1); ymr = 0; }
914       if (XMR > myWidth)  { width  = (GLsizei) (myWidth-xmr+1); }
915       if (YMR > myHeight) { height = (GLsizei) (myHeight-ymr+1); }
916
917       /* cas ou les 2 coins sont en dehors de la fenetre */
918       if (XMR < 0) { xmr = 0; width = height = 1; }
919       if (YMR < 0) { ymr = 0; width = height = 1; }
920       if (xmr > myWidth)  { xmr = 0; width = height = 1; }
921       if (ymr > myHeight) { ymr = 0; width = height = 1; }
922     }
923         }
924   }
925
926   glDrawBuffer (FrontToBack? GL_BACK : GL_FRONT);
927   glReadBuffer (FrontToBack? GL_FRONT : GL_BACK);
928   /* copie complete */
929   glRasterPos2i ((GLint) xmr, (GLint) ymr);
930   glCopyPixels ((GLint) xmr, (GLint) ymr, width, height, GL_COLOR);
931
932   EnableFeatures();
933
934   glMatrixMode (GL_PROJECTION);
935   glPopMatrix ();
936   glMatrixMode (GL_MODELVIEW);
937   glPopMatrix ();
938
939   glDrawBuffer (GL_BACK);
940 }
941
942 /*----------------------------------------------------------------------*/
943
944 //call_subr_displayCB
945 void OpenGl_Workspace::DisplayCallback (const Graphic3d_CView& ACView, int reason)
946 {
947   if( ACView.GDisplayCB )
948   {
949     Aspect_GraphicCallbackStruct callData;
950     callData.reason = reason;
951     callData.display = (DISPLAY*)myDisplay->GetDisplay();
952     callData.window = (WINDOW)myWindow;
953     callData.wsID = ACView.WsId;
954     callData.viewID = ACView.ViewId;
955     callData.gcontext = myGContext;
956
957     int status = (*ACView.GDisplayCB)( ACView.DefWindow.XWindow, ACView.GClientData, &callData );
958   }
959 }
960
961 /*----------------------------------------------------------------------*/