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