Salome HOME
Join modifications from branch BR_DEBUG_3_2_0b1
[modules/gui.git] / src / GLViewer / GLViewer_Widget.cxx
1 //  Copyright (C) 2005 OPEN CASCADE
2 //
3 //  This library is free software; you can redistribute it and/or
4 //  modify it under the terms of the GNU Lesser General Public
5 //  License as published by the Free Software Foundation; either
6 //  version 2.1 of the License.
7 //
8 //  This library is distributed in the hope that it will be useful,
9 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 //  Lesser General Public License for more details.
12 //
13 //  You should have received a copy of the GNU Lesser General Public
14 //  License along with this library; if not, write to the Free Software
15 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19 //  Author : OPEN CASCADE
20 //
21
22 // File:      GLViewer_Widget.cxx
23 // Created:   November, 2004
24
25 #include "GLViewer_Widget.h"
26 #include "GLViewer_ViewPort2d.h"
27 #include "GLViewer_Viewer2d.h"
28 #include "GLViewer_Compass.h"
29 #include "GLViewer_Grid.h"
30 #include "GLViewer_Object.h"
31 #include "GLViewer_CoordSystem.h"
32
33 #include <cmath>
34 using namespace std;
35
36 #include <qevent.h>
37 #include <qrect.h>
38
39 #include <qpixmap.h>
40 #include <qimage.h>
41 #include <qapplication.h>
42 #include <qintdict.h>
43 #include <qpaintdevicemetrics.h>
44 #include <qsize.h>
45 #include <qtooltip.h>
46
47 /*!
48   A constructor
49   Parameters using for QOGLWidget as is 
50 */
51 GLViewer_Widget::GLViewer_Widget( QWidget* parent, const char* name ):
52 QGLWidget( parent, 0/*, WRepaintNoErase | WResizeNoErase*/ )
53 {
54   myViewPort = ( GLViewer_ViewPort2d* )parent;
55
56   myXPan = 0.0;
57   myYPan = 0.0;
58   myZPan = 0.0;
59   myXScale = 1.0;
60   myYScale = 1.0;
61   myZScale = 1.0;
62   myRotationAngle = 0.0;
63   myRotationCenterX = 0.0;
64   myRotationCenterY = 0.0;
65   myRotationCenterZ = 1.0;
66   myRotationAnglePrev = 0.0;
67
68   myStart = GL_TRUE;
69
70   isExportMode = false;
71
72   //init();
73   setMouseTracking( true );
74 }
75
76 /*!
77   Destructor
78 */
79 GLViewer_Widget::~GLViewer_Widget()
80 {
81 }
82
83 /*!
84   \return offset parameters of Window in OpenGL global scene
85 */
86 void GLViewer_Widget::getPan( GLfloat& xPan, GLfloat& yPan, GLfloat& zPan )
87 {
88   xPan = myXPan;
89   yPan = myYPan;
90   zPan = myZPan;
91 }
92
93 /*!
94   A function for installing the  offset parameters of Window in OpenGL global scene
95 */
96 void GLViewer_Widget::setPan( GLfloat xPan, GLfloat yPan, GLfloat zPan )
97 {
98   myXPan = xPan;
99   myYPan = yPan;
100   myZPan = zPan;
101 }
102
103 /*!
104   \return scales on OpenGL scene along 3 directions in 2d scene zScale = 1.0
105 */
106 void GLViewer_Widget::getScale( GLfloat& xScale, GLfloat& yScale, GLfloat& zScale )
107 {
108   xScale = myXScale;
109   yScale = myYScale;
110   zScale = myZScale;
111 }
112
113 /*!
114   A function for installing the scales of OpenGL scene
115 */
116 void GLViewer_Widget::setScale( GLfloat xScale, GLfloat yScale, GLfloat zScale )
117 {
118   if ( xScale > 0 && yScale > 0 && zScale > 0 )
119   {
120     myXScale = xScale;
121     myYScale = yScale;
122     myZScale = zScale;
123   }
124 }
125
126 /*!
127   \return start point of curren rotation of Window in OpenGL global scene
128 */
129 void GLViewer_Widget::getRotationStart( GLfloat& rotationStartX,
130                                         GLfloat& rotationStartY,
131                                         GLfloat& rotationStartZ )
132 {
133     rotationStartX = myRotationStartX;
134     rotationStartY = myRotationStartY;
135     rotationStartZ = myRotationStartZ;
136 }
137
138 /*!
139   A function for installing the rotation angle of Window in OpenGL global scene in degree (Only in 2D)
140 */
141 void GLViewer_Widget::setRotationStart( GLfloat rotationStartX,
142                                         GLfloat rotationStartY,
143                                         GLfloat rotationStartZ )
144 {
145     myRotationStartX = rotationStartX;
146     myRotationStartY = rotationStartY;
147     myRotationStartZ = rotationStartZ;
148 }
149
150 /*!
151   \return parameters of rotation
152   \param rotationAngle - angle
153   \param rotationCenterX - center x
154   \param rotationCenterY - center y
155   \param rotationCenterZ - center z
156 */
157 void GLViewer_Widget::getRotation( GLfloat& rotationAngle,
158                                    GLfloat& rotationCenterX,
159                                    GLfloat& rotationCenterY,
160                                    GLfloat& rotationCenterZ )
161 {
162     rotationAngle = myRotationAngle;
163     rotationCenterX = myRotationCenterX;
164     rotationCenterY = myRotationCenterY;
165     rotationCenterZ = myRotationCenterZ;
166 }
167
168 /*!
169   Sets parameters of rotation
170   \param rotationAngle - angle
171   \param rotationCenterX - center x
172   \param rotationCenterY - center y
173   \param rotationCenterZ - center z
174 */
175 void GLViewer_Widget::setRotation( GLfloat rotationAngle,
176                                    GLfloat rotationCenterX,
177                                    GLfloat rotationCenterY,
178                                    GLfloat rotationCenterZ )
179 {
180     myRotationAngle = rotationAngle;
181     myRotationCenterX = rotationCenterX;
182     myRotationCenterY = rotationCenterY;
183     myRotationCenterZ = rotationCenterZ;
184 }
185
186
187 /*!
188   Sets image as background
189   \param filename - name of file
190 */
191 void GLViewer_Widget::setBackground( QString filename )
192 {
193     
194     //get image
195     QImage buf;
196     if ( !filename.isEmpty() && buf.load( filename ) ) 
197     {  // Load first image from file
198         isLoadBackground = true;
199         myBackgroundFile = filename;
200
201         myIW = buf.width();
202         myIH = buf.height();
203
204         myBackgroundSize = 64;
205         while( myBackgroundSize < myIW || myBackgroundSize < myIH)
206             myBackgroundSize = myBackgroundSize * 2;
207
208         GLubyte* pixels = new GLubyte[myBackgroundSize * myBackgroundSize * 4];
209
210         for( int i = 0; i < myBackgroundSize; i++ )
211         {            
212             for( int j = 0; j < myBackgroundSize; j++ )
213             {
214                 if( j < myIW && i < myIH )
215                 {
216                     pixels[i * myBackgroundSize * 4 + j * 4] = (GLubyte)qRed( buf.pixel(j,myIH - i - 1) );
217                     pixels[i * myBackgroundSize * 4 + j * 4 + 1]= (GLubyte)qGreen( buf.pixel(j,myIH - i - 1) );
218                     pixels[i * myBackgroundSize * 4 + j * 4 + 2] = (GLubyte)qBlue( buf.pixel(j,myIH - i - 1) );
219                 }
220                 else
221                 {
222                     pixels[i * myBackgroundSize * 4 + j * 4] = (GLubyte)0;
223                     pixels[i * myBackgroundSize * 4 + j * 4 + 1] = (GLubyte)0;
224                     pixels[i * myBackgroundSize * 4 + j * 4 + 2] = (GLubyte)0;
225                 }                
226                 pixels[i * myBackgroundSize* 4 + j * 4 +  3] = (GLubyte)255;
227             }
228         }
229
230         //initialize texture
231         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
232         glGenTextures(1, &texName);
233         glBindTexture(GL_TEXTURE_2D, texName);
234         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
235         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
236         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, myBackgroundSize , myBackgroundSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
237             pixels);
238
239         delete[] pixels;        
240     }
241 }
242
243 /*!
244   Adds tooltip
245   \param theString - tooltip text
246   \param theRect - tooltip rectangle
247 */
248 void GLViewer_Widget::addToolTip( QString theString, QRect theRect )
249 {
250     myToolTipRect = theRect;
251     QToolTip::add( this, myToolTipRect, theString );
252 }
253
254 /*!
255   Removes tooltip
256 */
257 void GLViewer_Widget::removeToolTip()
258 {
259     QToolTip::remove( this, myToolTipRect );
260 }
261
262 /*!
263   Initialization (redefined virtual from QGLWidget)
264 */
265 void GLViewer_Widget::initializeGL()
266 {
267     setAutoBufferSwap( true );
268
269     glShadeModel(GL_FLAT);
270     
271     //get image
272     QImage buf; 
273     QString aPicturePath = getenv("GLViewer__Background_Picture");
274     
275     if ( !aPicturePath.isEmpty() && buf.load( aPicturePath ) ) 
276     {  // Load first image from file
277         isLoadBackground = true;
278         setBackground( aPicturePath );       
279         
280     }
281     
282     else
283         isLoadBackground = false;
284 }
285
286 /*!
287   Paints content
288 */
289 void GLViewer_Widget::paintGL()
290 {
291     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
292     
293     glMatrixMode( GL_MODELVIEW );
294     glLoadIdentity();    
295
296     glRotatef( myRotationAngle, myRotationCenterX, myRotationCenterY, myRotationCenterZ );
297     glScalef( myXScale, myYScale, myZScale );
298     glTranslatef( myXPan, myYPan, myZPan );
299     
300     if( isLoadBackground )
301     {
302         glEnable(GL_TEXTURE_2D);
303         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
304         glBindTexture(GL_TEXTURE_2D, texName);
305         glBegin(GL_QUADS);
306
307         glTexCoord2f( 0.0, 0.0); glVertex3f( -myIW/2, -myIH/2, 0.0);    
308         glTexCoord2f( 0.0, (float)myIH/myBackgroundSize ); glVertex3f( -myIW/2, myIH/2, 0.0);
309         glTexCoord2f( (float)myIW/myBackgroundSize, (float)myIH/myBackgroundSize ); glVertex3f( myIW/2, myIH/2, 0.0);
310         glTexCoord2f( (float)myIW/myBackgroundSize, 0.0); glVertex3f( myIW/2, -myIH/2, 0.0);
311         
312         glEnd();
313         glFlush();
314         glDisable(GL_TEXTURE_2D);
315
316         glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
317     }
318
319
320     GLViewer_Grid* grid = myViewPort->getGrid();
321     if( grid )
322         grid->draw();
323
324     GLViewer_Viewer2d* v = ( GLViewer_Viewer2d* )getViewPort()->getViewFrame()->getViewer();
325     if( !isExportMode )
326         v->updateDrawers( GL_FALSE, myXScale, myYScale );
327     else
328         v->repaintView( getViewPort()->getViewFrame() );
329 }
330
331 /*!
332   Resets OpenGl parameters after resize
333   \param w - new width
334   \param h - new height
335 */
336 void GLViewer_Widget::resizeGL( int w, int h )
337 {
338
339   if( h < 1 ) h = 1;
340   if( w < 1 ) w = 1;
341   glViewport( 0, 0, w, h);
342
343   if( myStart )
344   {
345     myWidth = w;
346     myHeight = h;
347     myStart = GL_FALSE;
348   }
349
350   myViewPort->initResize( w, h );
351
352   glMatrixMode( GL_PROJECTION );
353   glLoadIdentity();
354   GLfloat w_c = w / 2., h_c = h / 2.; 
355
356   gluOrtho2D( -w_c, w_c, -h_c, h_c ); 
357
358   glMatrixMode( GL_MODELVIEW );
359   glLoadIdentity(); 
360 }
361
362 /*!
363   Provides repaint in export mode
364 */
365 void GLViewer_Widget::exportRepaint()
366 {
367     isExportMode = true;
368
369     paintGL();
370
371     isExportMode = false;
372 }
373
374 /*!
375   Custom paint event handler
376 */
377 void GLViewer_Widget::paintEvent( QPaintEvent* e )
378 {
379   QApplication::sendEvent( myViewPort, e );
380 }
381
382 /*!
383   Custom mouse move event handler
384 */
385 void GLViewer_Widget::mouseMoveEvent( QMouseEvent* e )
386 {
387   QApplication::sendEvent( myViewPort, e );
388 }
389
390 /*!
391   Custom mouse press event handler
392 */
393 void GLViewer_Widget::mousePressEvent( QMouseEvent* e )
394 {
395   QApplication::sendEvent( myViewPort, e );
396 }
397
398 /*!
399   Custom mouse release event handler
400 */
401 void GLViewer_Widget::mouseReleaseEvent( QMouseEvent* e )
402 {
403   QApplication::sendEvent( myViewPort, e );
404 }
405
406 /*!
407   Custom enter event handler
408 */
409 void GLViewer_Widget::enterEvent( QEvent* e )
410 {
411   updateGL();
412 }
413
414 /*!
415   Custom leave event handler
416 */
417 void GLViewer_Widget::leaveEvent( QEvent* e )
418 {
419   updateGL();
420 }
421
422
423 /*!
424   \return the hex code of digit < 16
425   \param c - digit
426 */
427 inline char hex( uchar c )
428 {
429   if( c<=9 )
430     return '0'+c;
431   else if( c < 16 )
432     return 'a' + c - 10;
433
434   return ' ';
435 }
436
437 /*!
438   Translates part of image inside rectangle from w1 to w2 and from h2 to h1 to PS format
439   \param hFile - PostScript file 
440   \param image - image to be tarnslated
441   \param w1 - x start position
442   \param w2 - x end position
443   \param h1 - y start position
444   \param h2 - y end position
445   \param aViewerCS - viewer co-ordinate system
446   \param aPSCS - paper co-ordinate system
447   \param a
448   \param b
449   \param c
450   \param d
451   \param dw
452   \param dh
453 */
454 void AddImagePart( QFile& hFile, QImage& image, int w1, int w2, int h1, int h2, 
455                    GLViewer_CoordSystem* aViewerCS, GLViewer_CoordSystem* aPSCS, 
456                    double a, double b, double c, double d, double dw, double dh )
457 {
458   if( aViewerCS && aPSCS )
459   {       
460     double width = w2-w1+1, height = h2-h1+1;
461     QString aBuffer = "", temp = "%1 %2 8 [ %3 %4 %5 %6 %7 %8 ]\n";
462     aBuffer += temp.arg( width ).arg( height ).
463                         arg( a ).arg( b ).arg( c ).arg( d ).
464                         arg( dw ).arg( dh );
465     aBuffer += "<\n";   
466     
467     char line[81]; line[80] = '\0'; int cur_index = 0;
468     int full = 0;
469     for( int i=h2; i>=h1; i-- )
470     {           
471       uchar* theCurLine = image.scanLine( i ), cur;
472       for( int j=w1; j<=w2; j++ )
473         for( int k=0; k<3; k++ )
474         {
475           cur = *(theCurLine+4*j+2-k);
476           *(line+cur_index) = hex( cur/16 ); //HI
477           *(line+cur_index+1) = hex( cur%16 ); //LO
478           full++;
479           cur_index+=2;
480           if( cur_index>=80 )
481           {
482             aBuffer += line;
483             aBuffer += "\n";
484             cur_index = 0;
485           }
486         }           
487     }
488     
489     aBuffer += "> false 3 colorimage\n\n";
490
491     hFile.writeBlock( aBuffer.ascii(), aBuffer.length() );
492   }
493 }
494
495 /*!
496   \return background rectangle in viewer CS
497 */
498 void GLViewer_Widget::getBackgroundRectInViewerCS( double& left, double& top, double& right, double& bottom )
499 {
500   left = -myIW/2; right = myIW/2; 
501   top = myIH/2; bottom = -myIH/2;
502 }
503
504 /*!
505   Translates background to PostScript
506   \param hFile - PostScript file 
507   \param aViewerCS - viewer co-ordinate system
508   \param aPSCS - paper co-ordinate system
509 */
510 void GLViewer_Widget::translateBackgroundToPS( QFile& hFile, GLViewer_CoordSystem* aViewerCS, GLViewer_CoordSystem* aPSCS )
511 {
512     QImage buf; 
513
514     if( aViewerCS && aPSCS && isLoadBackground && buf.load( myBackgroundFile ) )
515     {       
516         double a, b, c, d, dx, dy; //The preparation of transformation matrix
517
518         double width = buf.width(), height = buf.height();
519
520         double left, top, right, bottom;
521         getBackgroundRectInViewerCS( left, top, right, bottom );
522
523         double aax = left,  aay = bottom,
524                bbx = right, bby = bottom,
525                ccx = left,  ccy = top;             
526
527         aViewerCS->transform( *aPSCS, aax, aay );
528         aViewerCS->transform( *aPSCS, bbx, bby );
529         aViewerCS->transform( *aPSCS, ccx, ccy );       
530
531         a = ( bbx - aax ) / width;
532         b = ( ccx - aax ) / height;
533         c = ( bby - aay ) / width;
534         d = ( ccy - aay ) / height;
535
536         //Now we must find invert matrix 
537         double det = a*d-b*c,
538                newa = d/det,
539                newb = -c/det,
540                newc = -b/det,
541                newd = a/det;
542
543         a = newa; b = newb; c = newc; d = newd;
544
545         dx = -(a*aax+c*aay);
546         dy = -(b*aax+d*aay); //according to PS specification of coordinate transformation
547         
548         const int max = 133000; //The maximum length of string in PS
549         int dh = int( floor( double( max ) / ( 3.0*2.0*width ) ) );
550         for( int k=buf.height()-1; k>=0; k-=dh )
551             AddImagePart( hFile, buf, 0, buf.width()-1, QMAX( k-dh+1, 0 ), k,
552                           aViewerCS, aPSCS, a, b, c, d, dx, dy-(buf.height()-1-k) );
553     }
554 }
555
556 /*!
557   Translates image line with one color depth to line with other depth
558 */
559 void DecodeScanLine( int width, uchar* dest, int dest_depth, uchar* source, int source_depth )
560 {
561 #ifndef WIN32
562 typedef unsigned int WORD;
563 #endif
564
565     int aSize = width*dest_depth,
566         dw = aSize % 8;
567
568     if( dw )
569         aSize+=dw;
570
571     if( dest_depth==source_depth )
572         memcpy( dest, source, aSize/8 );
573     else
574     {
575         double r, g, b; WORD color;
576         for( int i=0; i<width; i++ )
577         {
578             color = 0;
579             switch( source_depth )
580             {
581                 case 16:
582                     memcpy( &color, source + 2*i, 2 );
583                     b = double( color & 0x001F ) / 31.0;
584                     g = double( ( color & 0x07E0 ) >> 5 ) / 63.0;
585                     r = double( ( color & 0xF800 ) >> 11 ) / 31.0;
586                     break;
587                 case 24: 
588                     b = double( *(source + 3*i) ) / 255.0;
589                     g = double( *(source + 3*i+1) ) / 255.0;
590                     r = double( *(source + 3*i+2) ) / 255.0;
591                     break;
592                 case 32:
593                     b = double( *(source + 4*i) ) / 255.0;
594                     g = double( *(source + 4*i+1) ) / 255.0;
595                     r = double( *(source + 4*i+2) ) / 255.0;
596                     break;
597             }
598             switch( dest_depth )
599             {
600                 case 16:
601                     color = WORD(b*31.0);
602                     color += (WORD(g*63.0)<<5);
603                     color += (WORD(r*31.0)<<11);
604                     memcpy( dest + 2*i, &color, 2 );
605                     break;
606                 case 24:
607                     *( dest + 3*i ) = (uchar)(255*b);
608                     *( dest + 3*i+1 ) = (uchar)(255*g);
609                     *( dest + 3*i+2 ) = (uchar)(255*r);
610                     break;
611                 case 32:
612                     *( dest + 4*i ) = (uchar)(255*b);
613                     *( dest + 4*i+1 ) = (uchar)(255*g);
614                     *( dest + 4*i+2 ) = (uchar)(255*r);
615                     *( dest + 4*i+3 ) = 0;
616                     break;
617             }
618         }
619     }
620 }
621
622 #ifdef WIN32
623 /*!
624   Translates background to EMF
625   \param dc - descriptor of EMF
626   \param aViewerCS - viewer co-ordinate system
627   \param aPSCS - paper co-ordinate system
628 */
629 void GLViewer_Widget::translateBackgroundToEMF( HDC dc, GLViewer_CoordSystem* aViewerCS, GLViewer_CoordSystem* aEMFCS )
630 {
631     QImage buf; 
632
633     if( aViewerCS && aEMFCS && isLoadBackground && buf.load( myBackgroundFile ) )
634     {       
635         double left, top, right, bottom;
636         getBackgroundRectInViewerCS( left, top, right, bottom );
637
638         double aRot = aViewerCS->getRotation();
639
640         double lx = left, ly = top;
641         aViewerCS->transform( *aEMFCS, lx, ly );
642
643         aViewerCS->setRotation( 0.0 ); //we switch off the rotation of CS
644
645         aViewerCS->transform( *aEMFCS, left, top );
646         aViewerCS->transform( *aEMFCS, right, bottom );
647         
648         int w = buf.width(), 
649             h = buf.height();
650
651         HDC aScrDC = GetDC( 0 );
652         HDC aCompDC = CreateCompatibleDC( aScrDC );
653         HBITMAP aBMP = CreateCompatibleBitmap( aScrDC, w, h );
654
655         BITMAP aBitInfo;
656         GetObject ( aBMP, sizeof(BITMAP), &aBitInfo );
657         int depth = aBitInfo.bmBitsPixel; //how many bits represent a color of one pixel
658
659         int aLineSize = w*depth;
660         int dw = aLineSize % 32; //scanline word aligning
661
662         if( dw )
663             aLineSize += 32-dw;
664
665         aLineSize /= 8;
666
667         BYTE* bits = new BYTE[aLineSize*h];
668         memset( bits, 0, aLineSize*h );
669         uchar* aLine = NULL;
670
671         for( int i=0; i<h; i++ )
672         {
673             aLine = buf.scanLine( i );
674             DecodeScanLine( w, bits+aLineSize*i, depth, aLine, buf.depth() );
675         }
676
677         SetBitmapBits( aBMP, aLineSize*h, bits );
678
679         HGDIOBJ old = SelectObject( aCompDC, aBMP ); 
680
681         XFORM aTrans;
682         GetWorldTransform( dc, &aTrans );
683         XFORM aRotTrans = aTrans;
684         double a = aRotTrans.eM11, 
685                b = aRotTrans.eM12, 
686                c = aRotTrans.eM21, 
687                d = aRotTrans.eM22;
688
689         aRotTrans.eM11 = a*cos( aRot )-b*sin( aRot ); //we multiply the current matrix with the rotation matrix 
690         aRotTrans.eM12 = a*sin( aRot )+b*cos( aRot );
691         aRotTrans.eM21 = c*cos( aRot )-d*sin( aRot );
692         aRotTrans.eM22 = c*sin( aRot )+d*cos( aRot );
693
694         a = aRotTrans.eM11; 
695         b = aRotTrans.eM12; 
696         c = aRotTrans.eM21; 
697         d = aRotTrans.eM22;
698
699         double det = a*d-b*c, //now we find the invert matrix 
700                newa = d/det,
701                newb = -c/det,
702                newc = -b/det,
703                newd = a/det;
704
705         a = newa; b = newb; c = newc; d = newd;
706
707         aRotTrans.eDx = lx -(a*left+c*top); //we find the dx and dy translating (left,top)->(lx,ly) -                                           
708         aRotTrans.eDy = ly -(b*left+d*top); //the real image of left-top corner of picture
709
710         SetWorldTransform( dc, &aRotTrans );
711         int res = StretchBlt( dc, left, top, right-left, bottom-top, aCompDC, 0, 0, w, h, SRCCOPY );
712         SetWorldTransform( dc, &aTrans );
713
714         SelectObject( aCompDC, old );
715
716         ReleaseDC( 0, aScrDC );
717         DeleteDC( aCompDC );
718         DeleteObject( aBMP );
719         delete[] bits;
720
721         aViewerCS->setRotation( aRot );
722     }
723 }
724 #endif