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