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