Commit | Line | Data |
---|---|---|
b311480e | 1 | // Created on: 2011-09-20 |
2 | // Created by: Sergey ZERCHANINOV | |
973c2be1 | 3 | // Copyright (c) 2011-2014 OPEN CASCADE SAS |
b311480e | 4 | // |
973c2be1 | 5 | // This file is part of Open CASCADE Technology software library. |
b311480e | 6 | // |
d5f74e42 | 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 | |
973c2be1 | 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. | |
b311480e | 12 | // |
973c2be1 | 13 | // Alternatively, this file may be used under the terms of Open CASCADE |
14 | // commercial license or contractual agreement. | |
7fd59977 | 15 | |
5f8b738e | 16 | #include <OpenGl_GlCore11.hxx> |
7fd59977 | 17 | |
7edf74fd | 18 | #include <OpenGl_FrameBuffer.hxx> |
7edf74fd | 19 | #include <TColStd_Array2OfReal.hxx> |
2166f0fa | 20 | #include <OpenGl_telem_util.hxx> |
c04c30b3 | 21 | #include <Graphic3d_GraphicDriver.hxx> |
22 | ||
23 | #include <OpenGl_PrinterContext.hxx> | |
24 | #include <OpenGl_Workspace.hxx> | |
25 | #include <OpenGl_View.hxx> | |
7fd59977 | 26 | |
c357e426 | 27 | #include <Standard_NotImplemented.hxx> |
28 | ||
4fe56619 | 29 | #if (defined(_WIN32) || defined(__WIN32__)) && defined(HAVE_FREEIMAGE) |
7edf74fd A |
30 | #include <NCollection_Handle.hxx> |
31 | #include <FreeImagePlus.h> | |
32 | #ifdef _MSC_VER | |
4fe56619 | 33 | #pragma comment( lib, "FreeImage.lib" ) |
34 | #pragma comment( lib, "FreeImagePlus.lib" ) | |
7edf74fd A |
35 | #endif |
36 | typedef NCollection_Handle<fipImage> FipHandle; | |
37 | #endif | |
7fd59977 | 38 | |
8cb69787 | 39 | #ifdef _WIN32 |
7edf74fd | 40 | |
8cb69787 | 41 | #ifndef HAVE_FREEIMAGE |
7fd59977 | 42 | |
c357e426 | 43 | // ======================================================================= |
44 | // function : initBitmapBuffer | |
45 | // purpose : init device independent bitmap to hold printing data | |
46 | // ======================================================================= | |
7edf74fd A |
47 | static void initBitmapBuffer (const HDC theMemoryDC, |
48 | HBITMAP &theMemoryBmp, | |
49 | const Standard_Integer theBmpWidth, | |
50 | const Standard_Integer theBmpHeight, | |
51 | VOID* &theBufferPtr) | |
52 | { | |
53 | // define compatible bitmap | |
54 | BITMAPINFO aBitmapData; | |
55 | memset (&aBitmapData, 0, sizeof (BITMAPINFOHEADER)); | |
56 | aBitmapData.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); | |
57 | aBitmapData.bmiHeader.biWidth = theBmpWidth; | |
58 | aBitmapData.bmiHeader.biHeight = theBmpHeight; | |
59 | aBitmapData.bmiHeader.biPlanes = 1; | |
60 | aBitmapData.bmiHeader.biBitCount = 24; | |
61 | aBitmapData.bmiHeader.biXPelsPerMeter = 0; | |
62 | aBitmapData.bmiHeader.biYPelsPerMeter = 0; | |
63 | aBitmapData.bmiHeader.biClrUsed = 0; | |
64 | aBitmapData.bmiHeader.biClrImportant = 0; | |
65 | aBitmapData.bmiHeader.biCompression = BI_RGB; | |
66 | aBitmapData.bmiHeader.biSizeImage = 0; | |
67 | ||
68 | // Create Device Independent Bitmap | |
69 | theMemoryBmp = CreateDIBSection (theMemoryDC, &aBitmapData, DIB_RGB_COLORS, | |
70 | &theBufferPtr, NULL, 0); | |
71 | } | |
8cb69787 | 72 | |
73 | #else /* HAVE_FREEIMAGE */ | |
74 | ||
c357e426 | 75 | // ======================================================================= |
76 | // function : imagePasteDC | |
77 | // purpose : copy the data from image buffer to the device context | |
78 | // ======================================================================= | |
7edf74fd | 79 | static bool imagePasteDC(HDC theDstDC, FipHandle theImage, int theOffsetX, |
4fe56619 | 80 | int theOffsetY, int theWidth, int theHeight, |
7edf74fd A |
81 | int theLeft = 0, int theTop = 0) |
82 | { | |
83 | // get image parameters | |
84 | BITMAPINFO* aBitmapData = theImage->getInfo (); | |
85 | SetStretchBltMode (theDstDC, STRETCH_HALFTONE); | |
4fe56619 | 86 | |
7edf74fd A |
87 | // organize blocks data passing if memory isn't enough to pass all the data |
88 | // at once | |
89 | int aLinesComplete = 0, aMaxBlockWidth = theHeight, aBlockWidth = 0, | |
90 | aPassed = 0, aInverseLine = 0, aScan = 0; | |
91 | BYTE *aDataPtr = 0; | |
92 | while (aMaxBlockWidth >= 1 && aLinesComplete < theHeight) | |
93 | { | |
94 | // how much lines still to pass | |
95 | aBlockWidth = theHeight - aLinesComplete; | |
96 | ||
97 | // normalize count of lines to pass to maximum lines count at one pass. | |
98 | if (aBlockWidth > aMaxBlockWidth) | |
99 | aBlockWidth = aMaxBlockWidth; | |
100 | ||
101 | // access image data at the start scan line, we need to calculate scan from | |
102 | // the bottom of image (image is bottom-left, the src coord is top-left) | |
103 | aInverseLine = theTop + aBlockWidth + aLinesComplete; | |
104 | aScan = theImage->getHeight() - aInverseLine; | |
105 | aDataPtr = theImage->getScanLine (aScan); | |
106 | if (!aDataPtr) | |
107 | return false; | |
108 | ||
109 | // try to pass block to the device | |
110 | if (aBlockWidth > 0) | |
111 | { | |
112 | // instead of banded output we provide blocked as it isn't always passed | |
113 | // to printer as it is expected | |
114 | aPassed = SetDIBitsToDevice (theDstDC, theOffsetX, | |
115 | theOffsetY + aLinesComplete, | |
116 | theWidth, aBlockWidth, theLeft, 0, | |
117 | 0, aBlockWidth, | |
118 | aDataPtr, aBitmapData, DIB_RGB_COLORS); | |
119 | ||
120 | // if result is bad, try to decrease band width | |
121 | if (aPassed != aBlockWidth) | |
122 | { | |
123 | aMaxBlockWidth = aMaxBlockWidth >> 1; | |
124 | aLinesComplete = 0; | |
125 | } | |
126 | else | |
127 | aLinesComplete += aBlockWidth; | |
128 | } | |
7fd59977 | 129 | } |
7edf74fd A |
130 | |
131 | // check for total failure | |
132 | if (aMaxBlockWidth < 1) | |
133 | return false; | |
134 | ||
135 | return true; | |
7fd59977 | 136 | } |
7fd59977 | 137 | |
c357e426 | 138 | // ======================================================================= |
139 | // function : imageStretchDC | |
140 | // purpose : copy pixels from image to dc by stretching them | |
141 | // ======================================================================= | |
7edf74fd A |
142 | static bool imageStretchDC(HDC theDstDC, FipHandle theImage, int theOffsetX, |
143 | int theOffsetY, int theWidth, int theHeight) | |
144 | { | |
145 | // access to raw image data | |
146 | BYTE *aDataPtr = theImage->accessPixels (); | |
147 | if (!aDataPtr) | |
148 | return false; | |
149 | ||
150 | // get image parameters | |
151 | unsigned int widthPx = theImage->getWidth (); | |
152 | unsigned int heightPx = theImage->getHeight (); | |
153 | BITMAPINFO* aBitmapData = theImage->getInfo (); | |
154 | SetStretchBltMode (theDstDC, STRETCH_HALFTONE); | |
4fe56619 | 155 | |
7edf74fd A |
156 | // pass lines and check if operation is succesfull |
157 | int aPassed = 0; | |
158 | aPassed = StretchDIBits (theDstDC, theOffsetX, theOffsetY, theWidth, | |
159 | theHeight, 0, 0, widthPx, heightPx, aDataPtr, | |
160 | aBitmapData, DIB_RGB_COLORS, SRCCOPY); | |
161 | ||
105aae76 | 162 | if ((unsigned)aPassed != heightPx) |
7edf74fd | 163 | return false; |
4fe56619 | 164 | |
7edf74fd A |
165 | return true; |
166 | } | |
8cb69787 | 167 | |
168 | #endif /* HAVE_FREEIMAGE */ | |
169 | ||
c357e426 | 170 | // ======================================================================= |
171 | // function : getNearestPowOfTwo | |
172 | // purpose : get the nearest power of two for theNumber | |
173 | // ======================================================================= | |
8cb69787 | 174 | static GLsizei getNearestPowOfTwo (const GLsizei theNumber) |
175 | { | |
176 | GLsizei aLast = 1; | |
177 | for (GLsizei p2 = 1; p2 <= theNumber; aLast = p2, p2 <<= 1); | |
178 | return aLast; | |
179 | } | |
47afc367 | 180 | |
c357e426 | 181 | // ======================================================================= |
182 | // function : getMaxFrameSize | |
183 | // purpose : get the maximum possible frame size | |
184 | // ======================================================================= | |
47afc367 | 185 | static void getMaxFrameSize(Standard_Integer& theWidth, |
186 | Standard_Integer& theHeight) | |
187 | { | |
188 | GLsizei aMaxX, aMaxY; | |
189 | GLint aVpDim[2]; | |
190 | GLint aTexDim = 2048; | |
191 | glGetIntegerv (GL_MAX_VIEWPORT_DIMS, (GLint*) &aVpDim); | |
192 | glGetIntegerv (GL_MAX_TEXTURE_SIZE, &aTexDim); | |
193 | (aVpDim[0] >= aTexDim) ? aMaxX = (GLsizei) aTexDim : | |
194 | aMaxX = getNearestPowOfTwo((GLsizei)aVpDim[0]); | |
195 | (aVpDim[1] >= aTexDim) ? aMaxY = (GLsizei) aTexDim : | |
196 | aMaxY = getNearestPowOfTwo((GLsizei)aVpDim[1]); | |
197 | ||
198 | theWidth = (Standard_Integer)aMaxX; | |
199 | theHeight = (Standard_Integer)aMaxY; | |
200 | } | |
47afc367 | 201 | |
c357e426 | 202 | // ======================================================================= |
203 | // function : fitDimensionsRatio | |
204 | // purpose : calculate correct width/height ratio for theWidth and | |
205 | // theHeight parameters | |
206 | // ======================================================================= | |
8cb69787 | 207 | static void fitDimensionsRatio (Standard_Integer& theWidth, |
208 | Standard_Integer& theHeight, | |
209 | const Standard_Real theViewRatio) | |
210 | { | |
211 | // set dimensions in accordance with the viewratio | |
212 | if (theHeight < theWidth/theViewRatio) | |
213 | theWidth = (Standard_Integer)(theHeight*theViewRatio); | |
214 | ||
215 | if (theWidth < theHeight*theViewRatio) | |
216 | theHeight = (Standard_Integer)(theWidth/theViewRatio); | |
217 | } | |
218 | ||
c357e426 | 219 | // ======================================================================= |
220 | // function : initBufferStretch | |
221 | // purpose : calculate initialization sizes for frame buffer | |
222 | // when the stretch algorithm is selected | |
223 | // ======================================================================= | |
47afc367 | 224 | static void initBufferStretch (Standard_Integer& theFrameWidth, |
225 | Standard_Integer& theFrameHeight, | |
226 | const int theViewWidth, | |
227 | const int theViewHeight) | |
228 | { | |
229 | ||
230 | // Calculate correct width/height for framebuffer | |
231 | Standard_Real aViewRatio = (Standard_Real)theViewWidth/theViewHeight; | |
232 | fitDimensionsRatio (theFrameWidth, theFrameHeight, aViewRatio); | |
233 | ||
234 | // downscale the framebuffer if it is too large | |
235 | Standard_Real aWidthRate = (Standard_Real)theFrameWidth /theViewWidth; | |
236 | Standard_Real aHeightRate = (Standard_Real)theFrameHeight/theViewHeight; | |
237 | ||
238 | if ((aWidthRate > 1 && aHeightRate > 1 && aWidthRate >= aHeightRate) || | |
239 | (aWidthRate > 1 && aHeightRate <= 1)) | |
240 | { | |
241 | theFrameWidth = (Standard_Integer)(theFrameWidth /aWidthRate); | |
242 | theFrameHeight = (Standard_Integer)(theFrameHeight/aWidthRate); | |
243 | } | |
244 | else if ((aWidthRate > 1 && aHeightRate > 1 && aWidthRate < aHeightRate) || | |
245 | (aWidthRate <= 1 && aHeightRate > 1)) | |
246 | { | |
247 | theFrameWidth = (Standard_Integer)(theFrameWidth /aHeightRate); | |
248 | theFrameHeight = (Standard_Integer)(theFrameHeight/aHeightRate); | |
249 | } | |
250 | } | |
c357e426 | 251 | |
252 | // ======================================================================= | |
253 | // function : initBufferTiling | |
254 | // purpose : calculate initialization sizes for frame buffer | |
255 | // when the tile algorithm is selected | |
256 | // ======================================================================= | |
47afc367 | 257 | static void initBufferTiling (Standard_Integer& theFrameWidth, |
258 | Standard_Integer &theFrameHeight, | |
259 | const int theViewWidth, | |
260 | const int theViewHeight) | |
261 | { | |
262 | // fit framebuffer into the printing area | |
263 | if (theFrameWidth > theViewWidth) | |
264 | theFrameWidth = theViewWidth; | |
265 | ||
266 | if (theFrameHeight > theViewHeight) | |
267 | theFrameHeight = theViewHeight; | |
268 | } | |
8cb69787 | 269 | |
270 | #endif /* _WIN32 */ | |
7fd59977 | 271 | |
c357e426 | 272 | // ======================================================================= |
273 | // function : Print | |
274 | // purpose : | |
275 | // ======================================================================= | |
276 | Standard_Boolean OpenGl_View::Print (const Aspect_Handle thePrinterDC, | |
277 | const Standard_Boolean theToShowBackground, | |
278 | const Standard_CString theFileName, | |
279 | const Aspect_PrintAlgo thePrintAlgorithm, | |
280 | const Standard_Real theScaleFactor) | |
2166f0fa | 281 | { |
47afc367 | 282 | #ifdef _WIN32 |
c357e426 | 283 | if (!myWorkspace->Activate()) |
7edf74fd | 284 | { |
2166f0fa SK |
285 | //MessageBox (NULL, "Print failed: can't setup the view for printing.", |
286 | // "The operation couldn't be completed.", MB_OK); | |
7edf74fd A |
287 | return Standard_False; |
288 | } | |
7fd59977 | 289 | |
c357e426 | 290 | Handle(OpenGl_Context) aCtx = myWorkspace->GetGlContext(); |
291 | ||
7edf74fd | 292 | // printer page dimensions |
c357e426 | 293 | HDC hPrnDC = (HDC) thePrinterDC; |
7edf74fd A |
294 | int devWidth = GetDeviceCaps (hPrnDC, HORZRES); |
295 | int devHeight = GetDeviceCaps (hPrnDC, VERTRES); | |
7fd59977 | 296 | |
7edf74fd A |
297 | // if context is actually a memory dc, try to retrieve bitmap dimensions |
298 | // (memory dc could be used for testing purposes) | |
299 | if (GetObjectType (hPrnDC) == OBJ_MEMDC) | |
300 | { | |
301 | // memory dc dimensions | |
302 | BITMAP aBitmapInfo; | |
303 | HBITMAP aMemoryBitmap = (HBITMAP) GetCurrentObject (hPrnDC, OBJ_BITMAP); | |
304 | if (aMemoryBitmap) | |
305 | if (GetObject (aMemoryBitmap, sizeof (BITMAP), &aBitmapInfo)) | |
306 | { | |
307 | devWidth = aBitmapInfo.bmWidth; | |
308 | devHeight = aBitmapInfo.bmHeight; | |
309 | } | |
310 | } | |
7fd59977 | 311 | |
7edf74fd A |
312 | Standard_Integer tempWidth = (Standard_Integer) devWidth; |
313 | Standard_Integer tempHeight = (Standard_Integer) devHeight; | |
7fd59977 | 314 | |
7edf74fd | 315 | // view dimensions |
c357e426 | 316 | int viewWidth = myWindow->Width(); |
317 | int viewHeight = myWindow->Height(); | |
7edf74fd | 318 | if (viewWidth == 0 || viewHeight == 0) |
7fd59977 | 319 | { |
2166f0fa SK |
320 | //MessageBox (NULL, "Print failed: can't setup the view for printing.", |
321 | // "The operation couldn't be completed.", MB_OK); | |
7edf74fd A |
322 | return Standard_False; |
323 | } | |
324 | ||
325 | // calculate correct width/height ratio | |
326 | Standard_Real viewRatio = (Standard_Real)viewWidth/viewHeight; | |
327 | fitDimensionsRatio(tempWidth, tempHeight, viewRatio); | |
328 | ||
329 | // width and height for printing area | |
330 | int width = (int) (tempWidth * theScaleFactor); | |
331 | int height = (int) (tempHeight * theScaleFactor); | |
332 | ||
333 | // device independent bitmap for the whole view | |
334 | #ifdef HAVE_FREEIMAGE | |
335 | FipHandle aViewImage = NULL; | |
336 | BYTE* aViewBuffer = NULL; | |
7fd59977 | 337 | #else |
7edf74fd A |
338 | HDC hMemDC = CreateCompatibleDC (hPrnDC); |
339 | HBITMAP hViewBitmap = NULL; | |
340 | HGDIOBJ hViewBitmapOld = NULL; | |
341 | VOID* aViewBuffer = NULL; | |
7fd59977 | 342 | #endif |
343 | ||
7edf74fd A |
344 | // Frame buffer initialization |
345 | OpenGl_FrameBuffer* aFrameBuffer = NULL; | |
c357e426 | 346 | OpenGl_FrameBuffer* aPrevBuffer = (OpenGl_FrameBuffer*)myFBO; |
7edf74fd A |
347 | Standard_Integer aFrameWidth (0), aFrameHeight (0), |
348 | aPrevBufferX (0), aPrevBufferY (0); | |
7fd59977 | 349 | |
c357e426 | 350 | bool IsTiling = (thePrintAlgorithm == Aspect_PA_TILE); |
2166f0fa | 351 | |
7edf74fd A |
352 | // try to use existing frame buffer |
353 | if (aPrevBuffer) | |
354 | { | |
355 | GLsizei aPrevWidth = aPrevBuffer->GetSizeX(); | |
356 | GLsizei aPrevHeight = aPrevBuffer->GetSizeY(); | |
357 | bool isUsable = false; | |
358 | ||
359 | // check if its possible to use previous frame buffer | |
360 | if (!IsTiling && aPrevWidth >= width && aPrevHeight >= height) | |
7fd59977 | 361 | { |
7edf74fd A |
362 | aFrameWidth = (Standard_Integer) width; |
363 | aFrameHeight = (Standard_Integer) height; | |
364 | isUsable = true; | |
7fd59977 | 365 | } |
7edf74fd | 366 | else if (IsTiling) |
7fd59977 | 367 | { |
b5ac8292 | 368 | // use previous frame buffer with its dimensions |
369 | aFrameWidth = aPrevWidth; | |
370 | aFrameHeight = aPrevHeight; | |
371 | isUsable = true; | |
7fd59977 | 372 | } |
373 | ||
7edf74fd A |
374 | // if it is enough memory for image paste dc operation |
375 | if (isUsable) | |
7fd59977 | 376 | { |
7edf74fd A |
377 | #ifdef HAVE_FREEIMAGE |
378 | // try to allocate fipImage and necessary resources | |
379 | fipImage* anImagePtr = new fipImage (FIT_BITMAP, aFrameWidth, | |
380 | aFrameHeight, 24); | |
381 | ||
c357e426 | 382 | // if allocated successfully |
7edf74fd A |
383 | if (anImagePtr->isValid()) |
384 | { | |
385 | aViewImage = anImagePtr; | |
386 | aViewBuffer = aViewImage->accessPixels (); | |
387 | } | |
388 | else | |
389 | delete anImagePtr; | |
390 | ||
391 | if (!aViewBuffer) | |
392 | { | |
393 | isUsable = false; | |
394 | aViewBuffer = NULL; | |
395 | aViewImage = NULL; | |
396 | } | |
7fd59977 | 397 | #else |
7edf74fd | 398 | // try to allocate compatible bitmap and necessary resources |
4fe56619 | 399 | initBitmapBuffer (hMemDC, hViewBitmap, |
7edf74fd A |
400 | aFrameWidth, aFrameHeight, aViewBuffer); |
401 | if (!aViewBuffer) | |
402 | { | |
403 | isUsable = false; | |
404 | if (hViewBitmap) | |
405 | DeleteObject (hViewBitmap); | |
406 | hViewBitmap = NULL; | |
407 | } | |
408 | else | |
409 | hViewBitmapOld = SelectObject (hMemDC, hViewBitmap); | |
410 | #endif | |
7fd59977 | 411 | } |
412 | ||
7edf74fd A |
413 | // use previous frame buffer |
414 | if (isUsable) | |
7fd59977 | 415 | { |
7edf74fd A |
416 | aPrevBufferX = aPrevWidth; |
417 | aPrevBufferY = aPrevHeight; | |
418 | aFrameBuffer = aPrevBuffer; | |
419 | aFrameBuffer->ChangeViewport (aFrameWidth, aFrameHeight); | |
7fd59977 | 420 | } |
7edf74fd | 421 | } |
7fd59977 | 422 | |
7edf74fd A |
423 | // if previous buffer cannot be used, try to init a new one |
424 | if (!aFrameBuffer) | |
425 | { | |
426 | aFrameBuffer = new OpenGl_FrameBuffer(); | |
7fd59977 | 427 | |
7edf74fd A |
428 | // try to create the framebuffer with the best possible size |
429 | Standard_Integer aMaxWidth(0), aMaxHeight(0); | |
430 | getMaxFrameSize (aMaxWidth, aMaxHeight); | |
431 | while (aMaxWidth > 1 && aMaxHeight > 1) | |
7fd59977 | 432 | { |
7edf74fd A |
433 | aFrameWidth = aMaxWidth; |
434 | aFrameHeight = aMaxHeight; | |
435 | ||
436 | // calculate dimensions for different printing algorithms | |
437 | if (!IsTiling) | |
438 | initBufferStretch (aFrameWidth, aFrameHeight, width, height); | |
439 | else | |
440 | initBufferTiling (aFrameWidth, aFrameHeight, width, height); | |
441 | ||
442 | // try to initialize framebuffer | |
c357e426 | 443 | if (aFrameBuffer->Init (aCtx, aFrameWidth, aFrameHeight)) |
7edf74fd A |
444 | { |
445 | #ifdef HAVE_FREEIMAGE | |
446 | // try to allocate fipImage and necessary resources | |
447 | fipImage* anImagePtr = new fipImage (FIT_BITMAP, aFrameWidth, | |
448 | aFrameHeight, 24); | |
449 | ||
c357e426 | 450 | // if allocated successfully |
7edf74fd A |
451 | if (anImagePtr->isValid()) |
452 | { | |
453 | aViewImage = anImagePtr; | |
454 | aViewBuffer = aViewImage->accessPixels (); | |
455 | } | |
456 | else | |
457 | delete anImagePtr; | |
458 | ||
459 | if (!aViewBuffer) | |
460 | { | |
c357e426 | 461 | aFrameBuffer->Release (aCtx.operator->()); |
7edf74fd A |
462 | aViewBuffer = NULL; |
463 | aViewImage = NULL; | |
464 | } | |
465 | else | |
466 | break; | |
467 | #else | |
468 | // try to allocate compatible bitmap and necessary resources | |
4fe56619 | 469 | initBitmapBuffer (hMemDC, hViewBitmap, |
7edf74fd A |
470 | aFrameWidth, aFrameHeight, aViewBuffer); |
471 | if (!aViewBuffer) | |
472 | { | |
473 | if (hViewBitmap) | |
474 | DeleteObject (hViewBitmap); | |
c357e426 | 475 | aFrameBuffer->Release (aCtx.operator->()); |
7edf74fd A |
476 | hViewBitmap = NULL; |
477 | } | |
478 | else | |
479 | { | |
480 | hViewBitmapOld = SelectObject (hMemDC, hViewBitmap); | |
481 | break; | |
482 | } | |
7fd59977 | 483 | #endif |
7edf74fd | 484 | } |
7fd59977 | 485 | |
7edf74fd A |
486 | // not initialized, decrease dimensions |
487 | aMaxWidth = aMaxWidth >> 1; | |
488 | aMaxHeight = aMaxHeight >> 1; | |
7fd59977 | 489 | } |
490 | ||
4fe56619 | 491 | // check if there are proper dimensions |
7edf74fd A |
492 | if (aMaxWidth <= 1 || aMaxHeight <= 1) |
493 | { | |
494 | MessageBox (NULL, "Print failed: can't allocate buffer for printing.", | |
c357e426 | 495 | "The operation couldn't be completed.", MB_OK); |
7edf74fd A |
496 | |
497 | if (aFrameBuffer) | |
498 | delete aFrameBuffer; | |
499 | #ifndef HAVE_FREEIMAGE | |
500 | if (hMemDC) | |
501 | DeleteDC (hMemDC); | |
7fd59977 | 502 | #endif |
503 | ||
7edf74fd A |
504 | return Standard_False; |
505 | } | |
506 | } | |
7fd59977 | 507 | |
7edf74fd | 508 | // setup printing context and viewport |
c357e426 | 509 | myWorkspace->PrinterContext() = new OpenGl_PrinterContext(); |
510 | myWorkspace->PrinterContext()->SetLayerViewport ((GLsizei )aFrameWidth, (GLsizei )aFrameHeight); | |
4fe56619 | 511 | GLint aViewPortBack[4]; |
5e27df78 | 512 | GLint anAlignBack = 1; |
7edf74fd A |
513 | glGetIntegerv (GL_VIEWPORT, aViewPortBack); |
514 | glGetIntegerv (GL_PACK_ALIGNMENT, &anAlignBack); | |
515 | glPixelStorei (GL_PACK_ALIGNMENT, 4); | |
516 | ||
517 | // start document if the printer context is not actually a memory dc | |
518 | // (memory dc could be used for testing purposes) | |
2166f0fa | 519 | DOCINFO di; |
7edf74fd A |
520 | if (GetObjectType (hPrnDC) == OBJ_DC) |
521 | { | |
c357e426 | 522 | // Initialize printing procedure |
7fd59977 | 523 | di.cbSize = sizeof(DOCINFO); |
7edf74fd | 524 | di.lpszDocName = "Open Cascade Document - print v3d view"; |
c357e426 | 525 | di.lpszOutput = theFileName; |
7fd59977 | 526 | |
7edf74fd A |
527 | // if can't print the document |
528 | if (StartDoc (hPrnDC, &di) <= 0 || StartPage (hPrnDC) <= 0) | |
529 | { | |
530 | MessageBox (NULL, "Print failed: printer can't start operation.", | |
531 | "The operation couldn't be completed.", MB_OK); | |
532 | #ifndef HAVE_FREEIMAGE | |
533 | if (hViewBitmap) | |
534 | { | |
535 | SelectObject (hMemDC, hViewBitmapOld); | |
536 | DeleteObject (hViewBitmap); | |
537 | } | |
538 | DeleteDC (hMemDC); | |
539 | #endif | |
7fd59977 | 540 | |
c357e426 | 541 | myWorkspace->PrinterContext().Nullify(); |
7edf74fd A |
542 | return Standard_False; |
543 | } | |
544 | } | |
7fd59977 | 545 | |
7edf74fd A |
546 | // calculate offset for centered printing |
547 | int aDevOffx = (int)(devWidth - width) /2; | |
548 | int aDevOffy = (int)(devHeight - height)/2; | |
7fd59977 | 549 | |
7edf74fd A |
550 | // operation complete flag |
551 | bool isDone = true; | |
2166f0fa SK |
552 | |
553 | // Set up status for printing | |
c357e426 | 554 | if (!theToShowBackground) |
555 | { | |
556 | myWorkspace->NamedStatus |= OPENGL_NS_WHITEBACK; | |
557 | } | |
2166f0fa | 558 | |
38a0206f | 559 | // switch to mono camera for image dump |
c357e426 | 560 | const Graphic3d_Camera::Projection aProjectType = myCamera->ProjectionType() != Graphic3d_Camera::Projection_Stereo |
561 | ? myCamera->ProjectionType() | |
38a0206f | 562 | : Graphic3d_Camera::Projection_Perspective; |
7edf74fd A |
563 | if (!IsTiling) |
564 | { | |
c357e426 | 565 | myWorkspace->PrinterContext()->SetScale ((GLfloat )aFrameWidth /viewWidth, (GLfloat )aFrameHeight/viewHeight); |
566 | redraw (aProjectType, aFrameBuffer); | |
1981cb22 | 567 | if (!myTransientDrawToFront) |
568 | { | |
569 | // render to FBO only if allowed to render to back buffer | |
ee51a9fe | 570 | myBackBufferRestored = Standard_True; |
571 | myIsImmediateDrawn = Standard_False; | |
c357e426 | 572 | redrawImmediate (aProjectType, NULL, aFrameBuffer); |
ee51a9fe | 573 | myBackBufferRestored = Standard_False; |
574 | myIsImmediateDrawn = Standard_False; | |
1981cb22 | 575 | } |
7edf74fd A |
576 | glReadPixels (0, 0, aFrameWidth, aFrameHeight, |
577 | GL_BGR_EXT, GL_UNSIGNED_BYTE, (GLvoid* )aViewBuffer); | |
578 | ||
579 | // copy result to the printer device and check for errors | |
580 | #ifdef HAVE_FREEIMAGE | |
581 | if (!aViewImage->rescale(width, height, FILTER_BICUBIC) || | |
582 | !imagePasteDC (hPrnDC, aViewImage, aDevOffx, aDevOffy, width, height)) | |
583 | isDone = imageStretchDC (hPrnDC, aViewImage, aDevOffx, aDevOffy, | |
584 | width, height); | |
7fd59977 | 585 | #else |
7edf74fd | 586 | if (width > aFrameWidth && height > aFrameHeight) |
7fd59977 | 587 | { |
7edf74fd | 588 | SetStretchBltMode (hPrnDC, STRETCH_HALFTONE); |
6de552e6 | 589 | isDone = (StretchBlt (hPrnDC, aDevOffx, aDevOffy, width, height, |
590 | hMemDC, 0, 0, aFrameWidth, aFrameHeight, SRCCOPY) != 0); // to avoid warning C4800 | |
7fd59977 | 591 | } |
7edf74fd | 592 | else |
7fd59977 | 593 | { |
6de552e6 | 594 | isDone = (BitBlt (hPrnDC, aDevOffx, aDevOffy, width, height, |
595 | hMemDC, 0, 0, SRCCOPY) != 0); // to avoid warning C4800 | |
7fd59977 | 596 | } |
7edf74fd A |
597 | #endif |
598 | } | |
599 | else | |
600 | { | |
601 | // calculate total count of frames and cropping size | |
602 | Standard_Integer aPxCropx = 0; | |
603 | Standard_Integer aPxCropy = 0; | |
4fe56619 | 604 | Standard_Integer aTotalx = |
7edf74fd | 605 | (Standard_Integer)floor ((float)width /aFrameWidth); |
4fe56619 | 606 | Standard_Integer aTotaly = |
7edf74fd A |
607 | (Standard_Integer)floor ((float)height/aFrameHeight); |
608 | if (width %aFrameWidth != 0) | |
7fd59977 | 609 | { |
7edf74fd A |
610 | aPxCropx = (aFrameWidth - width%aFrameWidth)/2; |
611 | aTotalx++; | |
7fd59977 | 612 | } |
7edf74fd | 613 | if (height%aFrameHeight != 0) |
7fd59977 | 614 | { |
7edf74fd A |
615 | aPxCropy = (aFrameHeight - height%aFrameHeight)/2; |
616 | aTotaly++; | |
7fd59977 | 617 | } |
7edf74fd A |
618 | |
619 | int anOddPixelx = (width %aFrameWidth) %2; | |
620 | int anOddPixely = (height%aFrameHeight)%2; | |
621 | ||
622 | // calculate scale factor for full frames | |
623 | Standard_Real aScalex = (Standard_Real)width /aFrameWidth; | |
624 | Standard_Real aScaley = (Standard_Real)height/aFrameHeight; | |
625 | ||
626 | // calculate and set the text scaling factor for printing context | |
627 | GLfloat aScaleRatex = (GLfloat)aFrameWidth /viewWidth; | |
628 | GLfloat aScaleRatey = (GLfloat)aFrameHeight/viewHeight; | |
c357e426 | 629 | myWorkspace->PrinterContext()->SetScale (aScaleRatex * (GLfloat )aScalex, aScaleRatey * (GLfloat )aScaley); |
7edf74fd A |
630 | |
631 | // initialize projection matrix for printer context | |
632 | TColStd_Array2OfReal aProj (0, 3, 0, 3); | |
633 | Standard_Real aDef = 0; | |
634 | aProj.Init (aDef); | |
635 | aProj(2,2) = 1.0; | |
636 | aProj(3,3) = 1.0; | |
637 | ||
638 | // projection matrix offsets for printer context | |
639 | // offsets are even numbers | |
640 | Standard_Real aOffsetx(0), aOffsety(0); | |
641 | aOffsetx = -(aTotalx-1); | |
642 | aOffsety = -(aTotaly-1); | |
643 | ||
644 | // rect of frame image that will be copied | |
645 | // and coordinates in view image where to put it | |
646 | Standard_Integer aLeft = 0, aRight = 0, aBottom = 0, aTop = 0; | |
647 | Standard_Integer aSubLeft = (Standard_Integer)aDevOffx; | |
648 | Standard_Integer aSubTop = (Standard_Integer)aDevOffy; | |
649 | ||
650 | // draw sequence of full frames | |
651 | for (int i = 0; i < aTotalx; i++) | |
7fd59977 | 652 | { |
7edf74fd A |
653 | // offsets are even numbers |
654 | aOffsety = -(aTotaly-1); | |
655 | aSubTop = (Standard_Integer)aDevOffy; | |
656 | ||
657 | // calculate cropped frame rect | |
658 | aLeft = (i == 0) ? aPxCropx : 0; | |
659 | aRight = (i == aTotalx-1) ? aFrameWidth-(aPxCropx+anOddPixelx) : | |
660 | aFrameWidth; | |
661 | ||
662 | for (int j = 0; j < aTotaly; j++) | |
663 | { | |
664 | // no offset for single frames | |
665 | aProj(3,0) = (aTotalx == 1) ? 0 : -aOffsetx; | |
666 | aProj(3,1) = (aTotaly == 1) ? 0 : aOffsety; | |
667 | ||
668 | // set projection matrix | |
669 | aProj(0,0) = aScalex; | |
670 | aProj(1,1) = aScaley; | |
c357e426 | 671 | myWorkspace->PrinterContext()->SetProjTransformation (aProj); |
7edf74fd A |
672 | |
673 | // calculate cropped frame rect | |
674 | aTop = (j == 0) ? aPxCropy : 0; | |
675 | aBottom = (j == aTotaly-1) ? aFrameHeight-(aPxCropy+anOddPixely) : | |
676 | aFrameHeight; | |
677 | ||
678 | // draw to the offscreen buffer and capture the result | |
c357e426 | 679 | redraw (aProjectType, aFrameBuffer); |
1981cb22 | 680 | if (!myTransientDrawToFront) |
681 | { | |
682 | // render to FBO only if forces to render to back buffer | |
ee51a9fe | 683 | myBackBufferRestored = Standard_True; |
684 | myIsImmediateDrawn = Standard_False; | |
c357e426 | 685 | redrawImmediate (aProjectType, NULL, aFrameBuffer); |
ee51a9fe | 686 | myBackBufferRestored = Standard_False; |
687 | myIsImmediateDrawn = Standard_False; | |
1981cb22 | 688 | } |
7edf74fd A |
689 | glReadPixels (0, 0, aFrameWidth, aFrameHeight, |
690 | GL_BGR_EXT, GL_UNSIGNED_BYTE, (GLvoid* )aViewBuffer); | |
691 | #ifdef HAVE_FREEIMAGE | |
692 | // cut out pixels that are out of printing area | |
693 | isDone = imagePasteDC (hPrnDC, aViewImage, aSubLeft, aSubTop, | |
694 | aRight-aLeft, aBottom-aTop, aLeft, aTop); | |
695 | #else | |
6de552e6 | 696 | isDone = (BitBlt (hPrnDC, aSubLeft, aSubTop, aRight-aLeft, aBottom-aTop, |
697 | hMemDC, aLeft, aTop, SRCCOPY) != 0); // to avoid warning C4800 | |
7edf74fd A |
698 | #endif |
699 | ||
700 | // stop operation if errors | |
701 | if (!isDone) | |
702 | break; | |
703 | ||
704 | // calculate new view offset for y-coordinate | |
705 | aOffsety += 2.0; | |
706 | aSubTop += aBottom-aTop; | |
707 | } | |
708 | ||
709 | // stop operation if errors | |
710 | if (!isDone) | |
711 | break; | |
4fe56619 | 712 | |
7edf74fd A |
713 | // calculate new view offset for x-coordinate |
714 | aOffsetx += 2.0; | |
715 | aSubLeft += aRight-aLeft; | |
7fd59977 | 716 | } |
7edf74fd | 717 | } |
7fd59977 | 718 | |
7edf74fd A |
719 | // complete printing or indicate an error |
720 | if (GetObjectType (hPrnDC) == OBJ_DC && isDone == true) | |
721 | { | |
722 | EndPage (hPrnDC); | |
723 | EndDoc (hPrnDC); | |
7fd59977 | 724 | } |
7edf74fd A |
725 | else if (isDone == false) |
726 | { | |
727 | MessageBox (NULL, "Print failed: insufficient memory or spool error.\nPlease use smaller printer resolution.", | |
728 | "The opeartion couldn't be completed.", MB_OK); | |
729 | if (GetObjectType (hPrnDC) == OBJ_DC) | |
730 | AbortDoc (hPrnDC); | |
731 | } | |
4fe56619 | 732 | |
7edf74fd | 733 | // return OpenGl to the previous state |
7edf74fd | 734 | glPixelStorei (GL_PACK_ALIGNMENT, anAlignBack); |
c357e426 | 735 | aFrameBuffer->UnbindBuffer (aCtx); |
4fe56619 | 736 | glViewport (aViewPortBack[0], aViewPortBack[1], |
7edf74fd A |
737 | aViewPortBack[2], aViewPortBack[3]); |
738 | if (aPrevBuffer) | |
96352003 | 739 | { |
7edf74fd | 740 | aPrevBuffer->ChangeViewport (aPrevBufferX, aPrevBufferY); |
96352003 | 741 | } |
7edf74fd | 742 | else |
96352003 | 743 | { |
c357e426 | 744 | aFrameBuffer->Release (aCtx.operator->()); |
7edf74fd | 745 | delete aFrameBuffer; |
96352003 | 746 | } |
7edf74fd A |
747 | |
748 | // delete resources | |
749 | #ifndef HAVE_FREEIMAGE | |
750 | if (hViewBitmap) | |
751 | { | |
752 | SelectObject (hMemDC, hViewBitmapOld); | |
753 | DeleteObject (hViewBitmap); | |
754 | } | |
755 | DeleteDC (hMemDC); | |
756 | #endif | |
757 | ||
2166f0fa | 758 | // Reset status after printing |
c357e426 | 759 | myWorkspace->NamedStatus &= ~OPENGL_NS_WHITEBACK; |
760 | myWorkspace->PrinterContext().Nullify(); | |
7edf74fd | 761 | return (Standard_Boolean) isDone; |
7fd59977 | 762 | |
47afc367 | 763 | #else // not _WIN32 |
c357e426 | 764 | Standard_NotImplemented::Raise ("OpenGl_View::Print is implemented only on Windows"); |
765 | myWorkspace->PrinterContext().Nullify(); | |
7edf74fd | 766 | return Standard_False; |
4fe56619 | 767 | #endif |
7fd59977 | 768 | } |