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