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