Announcement

Collapse
No announcement yet.

Rotating Bitmaps - some fun

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Rotating Bitmaps - some fun

    While I can't give away any secrets here (since the code may end up in a future product), I would like to demonstrate that rotating bitmaps can be done using the GDI (no GDI+ required).

    Windows 2000, XP and Vista have this ability already and it does not require GDI plus.

    You can download an actual DDT app that does this using the link below.

    If you have any doubts, Windows is actually the one rotating the image and not some special DIB code of mine.

    If you can figure this one out on your own, have fun!

    I just wanted to show it can be done and Windows (the GDI) can do it.
    Attached Files
    Chris Boss
    Computer Workshop
    Developer of "EZGUI"
    http://cwsof.com
    http://twitter.com/EZGUIProGuy

  • #2
    The actual code that accomplishs the rotate image and draw is two subroutines which consist of only 45 lines of code.

    No DIB sections are used!
    No Get DIB type calls are made!

    Two memory DC's and one Window DC are used.

    Thats about all I can give you, sorry.

    Have fun reverse engineering the code!
    Chris Boss
    Computer Workshop
    Developer of "EZGUI"
    http://cwsof.com
    http://twitter.com/EZGUIProGuy

    Comment


    • #3
      PlgBlt + double buffer

      ...
      Patrice Terrier
      www.zapsolution.com
      www.objreader.com
      Addons: GDImage.DLL 32/64-bit (Graphic library), WinLIFT.DLL 32/64-bit (Skin Engine).

      Comment


      • #4
        No, I didn't use PlgBlt.

        Interesting API though. I wasn't familiar with that one.
        Chris Boss
        Computer Workshop
        Developer of "EZGUI"
        http://cwsof.com
        http://twitter.com/EZGUIProGuy

        Comment


        • #5
          Now, I should note that I only have to use double memory DC's (buffers) so I can draw the white background around the image. If I were only drawing the image in rotation and didn't care about the left over of the image when I rotated it, then I could draw directly into the window DC without double buffers.
          Chris Boss
          Computer Workshop
          Developer of "EZGUI"
          http://cwsof.com
          http://twitter.com/EZGUIProGuy

          Comment


          • #6
            I don't understand why you would make such a post? Kinda sounds like a "naw naw naw, I can code something that you can't..... but I'm not going to show you... I just want to show that I am superior to everyone else here".

            I just don't get it.

            I know that I'm new here but this thread just seems very out of place from most other posts I have been reading here where everyone bends over backwards to share.

            Comment


            • #7
              Some of my code may end up in proprietary projects and some is less likely to. The code which is more likely to end up in proprietary projects I avoid posting, while other code I post quite a bit, especially DDT code.

              That said, I was just so excited to see that you can rotate bitmaps using the GDI alone (XP and Vista), I wanted to post an example app (exe) so others can play with it.

              I may give a away a few secrets, but I would like to encourage others to see if they can figure the API's to accomplish this.

              A good place to start is to use the Microsoft Depends utility to see what API's the app uses.

              Also by seeing an actual app which does this (the EXE), it encourages others to dig deeper to understand how it is done.

              I'll give one clue though:

              Many are familiar with how Windows works with device units and logical units for coordinates. What many do not appreciate is that Windows has a number of other means to modify the coordinate systems in use, such as viewports and world transformations. These have drastic effects on many GDI drawing functions.

              Also sample code is found in the Windows SDK docs. They just do some drawing with lines, but I went further in drawing bitmaps.
              The sample code is enough to start learning the basic concepts.

              I went a lot further with a lot of expeirmenting. (which is why the source code will likely end up in a proprietary application)

              Thats the clue!

              Have fun!

              You may not appreciate this post, but download the zip file and run the EXE and see if it isn't exciting to see.
              I have wondered for years how to do this and have found little info on it.
              GDI plus seemed like the only way to accomplish this.
              It turns out GDI plus uses some of the same API's I am using and it is quite possible GDI plus is letting the operating system (GDI) handle some of the tough stuff.

              Also rotation is only the beginning. The API's used allow even more.
              Last edited by Chris Boss; 27 May 2009, 08:07 PM.
              Chris Boss
              Computer Workshop
              Developer of "EZGUI"
              http://cwsof.com
              http://twitter.com/EZGUIProGuy

              Comment


              • #8
                The following BCX function (which should be trivial to convert to PB) returns a HBITMAP based on the SetWorldTransform function in 52 lines of code.


                Code:
                FUNCTION RotateBmp(hBitmap AS HBITMAP, Angle AS SINGLE, clrBack AS COLORREF) AS HBITMAP
                '*****************************************************************************************
                ' Original MFC code by Zafir Anjum August 5, 1998
                ' http://www.codeguru.com/cpp/g-m/bitmap/specialeffects/article.php/c1743/
                ' Converted to BCX by Kevin Diggins with help by Mike Henning
                ' Specify rotation angle using decimal degrees instead of radians
                '*****************************************************************************************
                  
                  DIM sourceDC AS HDC   
                  DIM destDC   AS HDC
                
                  sourceDC = CreateCompatibleDC(NULL) 
                  destDC   = CreateCompatibleDC(NULL)
                
                  
                  DIM bm AS BITMAP
                  GetObject(hBitmap, SIZEOF(bm),&bm)   ' Get logical coordinates
                
                  DIM cosine AS SINGLE
                  DIM sine   AS SINGLE
                
                  cosine = COS(Angle * 0.017453)
                  sine   = SIN(Angle * 0.017453)
                
                  ' Compute dimensions of the resulting bitmap
                  ' First get the coordinates of the 3 corners other than origin
                
                  DIM RAW x1 = (bm.bmHeight * sine)
                  DIM RAW y1 = (bm.bmHeight * cosine)
                  DIM RAW x2 = (bm.bmWidth * cosine + bm.bmHeight * sine)
                  DIM RAW y2 = (bm.bmHeight * cosine - bm.bmWidth * sine)
                  DIM RAW x3 = (bm.bmWidth * cosine)
                  DIM RAW y3 = (-bm.bmWidth * sine)
                
                  DIM RAW minx = MIN(0,MIN(x1, MIN(x2,x3)))
                  DIM RAW miny = MIN(0,MIN(y1, MIN(y2,y3)))
                  DIM RAW maxx = MAX(0,MAX(x1, MAX(x2,x3)))
                  DIM RAW maxy = MAX(0,MAX(y1, MAX(y2,y3)))
                
                  DIM RAW w = 1 + maxx - minx
                  DIM RAW h = 1 + maxy - miny
                
                  '*************************************
                  ' Create a bitmap to hold the result
                  '*************************************
                
                  DIM RAW WinDC AS HDC
                  DIM RAW hbmResult AS HBITMAP
                  WinDC = GetDC(NULL)
                  hbmResult = CreateCompatibleBitmap(WinDC, w, h)
                  ReleaseDC(NULL, WinDC)
                
                  DIM RAW hbmOldSource AS HBITMAP
                  DIM RAW hbmOldDest AS HBITMAP
                
                  hbmOldSource = SelectObject(sourceDC, hBitmap)
                  hbmOldDest = SelectObject(destDC, hbmResult)
                
                  ' Draw the background color before we change mapping mode
                
                  DIM RAW hbrBack AS HBRUSH
                  DIM RAW hbrOld  AS HBRUSH
                
                  hbrBack = CreateSolidBrush(clrBack)
                  hbrOld = SelectObject(destDC, hbrBack)
                
                  PatBlt(destDC,0, 0, w, h, PATCOPY)
                
                  DeleteObject(SelectObject(destDC, hbrOld))
                
                  ' use world transform to rotate the bitmap
                
                  SetGraphicsMode(destDC, GM_ADVANCED)
                
                  DIM xform AS XFORM
                
                  xform.eM11 = cosine
                  xform.eM12 = -sine
                  xform.eM21 = sine
                  xform.eM22 = cosine
                  xform.eDx = -minx
                  xform.eDy = -miny
                
                  SetWorldTransform(destDC, &xform)
                
                  '  Now do the actual rotating - a pixel at a time
                
                  BitBlt(destDC,0,0,bm.bmWidth, bm.bmHeight, sourceDC, 0, 0, SRCCOPY )
                
                  ' Restore DCs
                  SelectObject(sourceDC, hbmOldSource)
                  SelectObject(destDC, hbmOldDest)
                
                  FUNCTION = hbmResult
                END FUNCTION

                Comment


                • #9
                  Originally posted by Bill Fulton View Post
                  I know that I'm new here but this thread just seems very out of place from most other posts I have been reading here where everyone bends over backwards to share.
                  You are new here, Bill. After you've been here awhile, you'll find out two things:

                  1) Chris is one of the most sharingest and knowledgeablest guys on the forum, often going to great lengths to explain how things work.

                  And:

                  2) It's not unusual at all for guys here to present challenges to the rest of us. (though in this case probably only a very select few will be able to take up the tossed gauntlet.)

                  ===========================================
                  We must question the story logic
                  of having an all-knowing all-powerful God,
                  who creates faulty humans,
                  and then blames them for his own mistakes.
                  ~ Gene Roddenberry
                  ===========================================
                  Last edited by Gösta H. Lovgren-2; 27 May 2009, 08:51 PM.
                  It's a pretty day. I hope you enjoy it.

                  Gösta

                  JWAM: (Quit Smoking): http://www.SwedesDock.com/smoking
                  LDN - A Miracle Drug: http://www.SwedesDock.com/LDN/

                  Comment


                  • #10
                    Kevin is on the right path!

                    I should note though that Kevins code uses some extra calculations, mine does not.
                    I use a slightly different technique to get the bitmap in the right position when drawn.

                    As you can see though from his code, BitBlt does the hard work for you.

                    The API handles transformations differently for BitBlt than it does for things like lines and shapes.
                    I am not sure why.
                    Last edited by Chris Boss; 27 May 2009, 09:28 PM.
                    Chris Boss
                    Computer Workshop
                    Developer of "EZGUI"
                    http://cwsof.com
                    http://twitter.com/EZGUIProGuy

                    Comment


                    • #11
                      Geeh, read about this during 32bit VB4 in Dan Appleman's book..

                      Seriously, if this makes rotation fast.. neat.
                      I believe it was slow and we often rotate not in 90deg steps but in between.
                      I guess this api is not that fast(?)
                      hellobasic

                      Comment


                      • #12
                        Bill,

                        Kinda sounds like a "naw naw naw, I can code something that you can't...
                        Chris, has been looking for long how to perform smooth image rotation, and he likes to challenge with a few of us ...

                        For those interrested, Semen Matusovskiy posted here an example showing one solution to perform bitmap rotation many years ago.

                        Both Chris and Kev code are based on the SetWorldTransform, PlgBlt is another one.

                        VC++ example
                        More insights
                        Using PlgBlt
                        Delphi example using PlgBlt
                        And now in Basic

                        I have been playing with several solutions myself, and my GDImage uses the best of the four methods i know.

                        ...
                        Patrice Terrier
                        www.zapsolution.com
                        www.objreader.com
                        Addons: GDImage.DLL 32/64-bit (Graphic library), WinLIFT.DLL 32/64-bit (Skin Engine).

                        Comment


                        • #13
                          I have played with Semens code gleaned from the forums.

                          It works with a DIB and manipulates pixel by pixel.

                          I have attached a zip file to this post, with a modified version of his code, both an exe and the basic source code.

                          I don't fully understand his code, so I just changed a few things so I could time the rotation.

                          It runs nearly half the speed of my latest code using world transformations. The Windows GDI

                          World Transformations are much faster and well optimized.

                          I do think one could write a routine which is faster by optimizing it with assembler or using precalculated tables.

                          Semens code appears to be based on the technique found on the web page noted in Kevens code (which is only a translation of someone elses code) found here:

                          http://www.codeguru.com/cpp/g-m/bitm...icle.php/c1743


                          Both my first zip file (exe) using world transformations and Semens code are rotating an image 1 degree at a time (360 iterations) and time the results. The images are not the same but close in size though (give or take a little).
                          Attached Files
                          Chris Boss
                          Computer Workshop
                          Developer of "EZGUI"
                          http://cwsof.com
                          http://twitter.com/EZGUIProGuy

                          Comment


                          • #14
                            The interesting thing about all of this is that I have the impression that even GDI plus uses this technique. When you examine the GDI plus dll using Depends (shows all dependencies of a DLL or EXE), it is calling the world transformation API's itself.

                            I can't know for sure, but it is quite possible the rotation code was already in Windows (NT,200,XP, Vista) and GDI plus is simply accessing it.

                            The downside to this is that these API's are not in Windows 95/98/ME.
                            Chris Boss
                            Computer Workshop
                            Developer of "EZGUI"
                            http://cwsof.com
                            http://twitter.com/EZGUIProGuy

                            Comment


                            • #15
                              it is calling the world transformation API's itself
                              I don't know if GDIPLUS is using the initial NT world transformation, however it is using matrix world transformation for sure (you are getting hot)
                              Patrice Terrier
                              www.zapsolution.com
                              www.objreader.com
                              Addons: GDImage.DLL 32/64-bit (Graphic library), WinLIFT.DLL 32/64-bit (Skin Engine).

                              Comment


                              • #16
                                >The downside to this is that these API's are not in Windows 95/98/ME.

                                There is no downside!
                                hellobasic

                                Comment


                                • #17
                                  Originally posted by Edwin Knoppert View Post
                                  >The downside to this is that these API's are not in Windows 95/98/ME.

                                  There is no downside!
                                  Yes, may Windows 95/98/ME rest in peace.
                                  Paul Squires
                                  FireFly Visual Designer (for PowerBASIC Windows 10+)
                                  Version 3 now available.
                                  http://www.planetsquires.com

                                  Comment


                                  • #18
                                    Of course not as good as GDImage

                                    :laugh:
                                    Patrice Terrier
                                    www.zapsolution.com
                                    www.objreader.com
                                    Addons: GDImage.DLL 32/64-bit (Graphic library), WinLIFT.DLL 32/64-bit (Skin Engine).

                                    Comment


                                    • #19
                                      Originally posted by Chris Boss View Post
                                      Semens code appears to be based on the technique found on the web page noted in Kevens code (which is only a translation of someone elses code) found here:
                                      http://www.codeguru.com/cpp/g-m/bitm...icle.php/c1743
                                      Chris ... Unlike your purported BIG SECRET, I named my sources:

                                      '*********************************************************************************
                                      ' Original MFC code by Zafir Anjum August 5, 1998
                                      ' http://www.codeguru.com/cpp/g-m/bitm...cle.php/c1743/
                                      ' Converted to BCX by Kevin Diggins with help by Mike Henning
                                      ' Specify rotation angle using decimal degrees instead of radians
                                      '*********************************************************************************

                                      Comment


                                      • #20
                                        How do these "new" methods compare with just the old fashioned way of writing the rotation code in BASIC?

                                        The following code appears to be 50% faster than the code in post 13 of this thread and it rotates the full sized image, rather than shinking it to half size as that code appears to do.


                                        Paul.
                                        Code:
                                        #COMPILE EXE
                                        #BREAK ON
                                        
                                        
                                        #INCLUDE "win32api.inc"
                                        
                                        %xWindowSize = 400
                                        %yWindowSize = 400
                                        
                                        %xRotateOffset=200
                                        %yRotateOffset=200
                                        
                                        FUNCTION PBMAIN () AS LONG
                                        LOCAL hInitialBMP,hRotatedBMP,hWindow AS DWORD
                                        LOCAL InitialBMPstring, RotatedBMPstring AS STRING
                                        LOCAL x,y,r,t AS LONG
                                        LOCAL sina, cosa AS EXT
                                                             
                                        
                                        nFile& = FREEFILE
                                        OPEN "rose.bmp" FOR BINARY AS nFile&
                                        GET #nFile&, 19, xSize& 'nWidth&
                                        GET #nFile&, 23, ySize& 'nHeight&
                                        CLOSE nFile&
                                        
                                                         
                                        GRAPHIC WINDOW "Rotation test",200,200, %xWindowSize,%yWindowSize TO hWindow
                                        GRAPHIC ATTACH hWindow,0
                                        GRAPHIC GET BITS TO RotatedBMPstring
                                        GRAPHIC DETACH
                                        
                                        GRAPHIC BITMAP LOAD "rose.bmp", xSize&, ySize&, %HALFTONE  TO hInitialBMP
                                        GRAPHIC ATTACH hInitialBMP,0
                                        GRAPHIC GET BITS TO InitialBMPstring
                                        GRAPHIC DETACH
                                        
                                        THREAD CREATE RedrawGraphics (hWindow) TO hThread&
                                        
                                        DIM initial&(1 TO xSize&,1 TO ySize&) AT STRPTR(InitialBMPstring)+8
                                        DIM rotated&(1 TO %xWindowSize,1 TO %yWindowSize) AT STRPTR(RotatedBMPstring)+8
                                        
                                        
                                        t1&=TIMER*100
                                        
                                        FOR a& = 0 TO 360    'degrees
                                            
                                        angle##=a&/(180/3.141592653589)
                                        cosa=COS(angle##)
                                        sina=SIN(angle##)
                                        
                                        FOR x&=1 TO %xWindowSize
                                            FOR y& = 1 TO %yWindowSize
                                        
                                               oldx& =(x& - %xRotateOffset) *cosa + (y& - %yRotateOffset)*sina + %xRotateOffset -50
                                           
                                               oldy& =(y& - %yRotateOffset) *cosa - (x& - %xRotateOffset)*sina + %yRotateOffset  -50
                                                
                                                              
                                               IF oldx&>0 AND oldx& < xSize&  AND oldy&>0 AND oldy&< ySize& THEN
                                                    Rotated&(x&,y&)   =  Initial&(oldx& ,oldy&)
                                                ELSE
                                                    Rotated&(x&,y&)   =  %WHITE
                                                    
                                                END IF
                                           
                                        
                                            NEXT
                                        NEXT
                                         
                                        
                                        GRAPHIC ATTACH hWindow,0 ,REDRAW
                                        GRAPHIC SET BITS RotatedBMPstring
                                        GRAPHIC DETACH
                                                                        
                                        SLEEP 0
                                        NEXT
                                        
                                        t2&=TIMER*100
                                        PRINT "Time Taken = "(t2&-t1&)/100
                                        PRINT "Frames per second ="; 360/((t2&-t1&)/100 )
                                        
                                        QuitAll& =1
                                        
                                        THREAD CLOSE hThread& TO lResult&
                                        WAITKEY$
                                        
                                        END FUNCTION
                                        
                                        
                                        
                                        'This is a flag intended to cause all otherwise non-terminating threads to quit
                                        GLOBAL QuitAll&
                                        
                                        'This routine and the main program must use GRAPHIC ATTACH WindowID,0,REDRAW but the main program shouldn't
                                        'issue a GRAPHIC REDRAW command in normal use. Leave that up to this thread.
                                        FUNCTION RedrawGraphics(BYVAL WinID AS DWORD) AS DWORD
                                        
                                        GRAPHIC ATTACH WinID,0,REDRAW
                                        
                                        DO
                                        
                                            GRAPHIC REDRAW
                                            SLEEP 1
                                        LOOP UNTIL QuitAll&
                                        
                                        END FUNCTION

                                        Comment

                                        Working...
                                        X