Discussions related to Visual Prolog
Harrison Pratt
VIP Member
Posts: 439
Joined: 5 Nov 2000 0:01

Rotate a bitmap by an arbitrary angle

Unread post by Harrison Pratt »

I am just starting to explore GDI+ and the depth of my understanding is very shallow. By way of exploration, I decided to tackle rotating a resource bitmap by an arbitrary angle and still have not fully absorbed how pointers, handles, bitmapIDs, and actual images are managed by VP and GDI.

My little test code below is intended to load a bitmap, draw it, rotate it 90 degrees, draw that rotated bitmap, then rotate a copy of the original bitmap 33.3 degrees and draw that bitmap. I am stumped on how to handle the last part of the test--the first two parts are simply to verify that I can draw something.

Code: Select all

predicates     onPaint : window::paintResponder. clauses         onPaint( _Source, _Rectangle, GDI):-             Pnt1 = pnt(100,100),             Pnt2 = pnt(150,100),             Pnt3 = pnt(200,100),             Angle = 33.3,  % angle to rotate bitmap below               % Bitmap name in resources = "BoatStbdTack"             PicB = vpi::pictGetFromRes( resourceIdentifiers::idb_boatstbdtack ),   % returns a PICTURE (a POINTER to the the bitmap)             GDI:pictDraw( PicB, Pnt1, rop_SrcAnd ),  % operates on the POINTER to the picture               PtrB90 = vpi::pictRotate(PicB,pictRotate_90deg ),  % counter-clockwise rotation, only in 90 degree steps             GDI:pictDraw( PtrB90, Pnt2, rop_SrcAnd ),               /*                     This is where I am stumped.                       How can I create BMP_ToROTATE (an actual bitmap) to rotate using rotateImage/2 below ?                       Perhaps rotateImage/2 should be modified to work with pictures (pointers) instead of actual bitmaps ...             */               NewRotBMP = rotateImage(  BMP_ToROTATE, Angle ),             GDI:pictDraw( NewRotBMP ,Pnt3,rop_SrcAnd),             succeed.
The predicate to rotate a bitmap is below:

Code: Select all

class predicates     rotateImage : (  bitmap, real AngleToRotate ) -> bitmap RotatedBMP. clauses     rotateImage(  BMP, Angle ) = RotatedBMP :-         G = graphics::createFromImage( BMP ),    % create new graphics canvas         % move rotation point to center of image         G:translateTransform( BMP:width/2, BMP:height /2 ),         % rotate the graphics         G:rotateTransform( Angle ),         % move the image back         G:translateTransform( - BMP:width/2, - BMP:height/2 ),         % create bitmap from rotated graphics         RotatedBMP = bitmap::createFromGraphics( BMP:width, BMP:height, G).
Paul Cerkez
VIP Member
Posts: 106
Joined: 6 Mar 2000 0:01

Unread post by Paul Cerkez »

Harrison,
I did exactly this a few years back. let me see if I can find my code.

P.
AI Rules!
P.
User avatar
Thomas Linder Puls
VIP Member
Posts: 1398
Joined: 28 Feb 2000 0:01

Unread post by Thomas Linder Puls »

Have you examined the GDI+ example that comes with Visual Prolog?

Also you didn't tell what happened (exception, nothing, wrong result, ...).
Regards Thomas Linder Puls
PDC
Harrison Pratt
VIP Member
Posts: 439
Joined: 5 Nov 2000 0:01

Unread post by Harrison Pratt »

I did work through the GDI+ example and had no problems there. I can draw lines and shapes without problem, and the test code I submitted draws the resource bit map normally and rotated without problem. I also remembered to call

Code: Select all

appGDI::appGdiStart()
in the taskWindow:onShow/2 clause.

My confusion is with how to obtain a 'bitmap' structure to pass to this line of code:

Code: Select all

NewRotBMP = rotateImage(  BmpToRotate, Angle ),
Depending upon which GDI-related or VIP predicates I use to retrieve what I expect (hope) is a bitmap, I get compiler error 504, where XXX is 'picture' or 'bitmapID' etc.,

Code: Select all

e504            The expression has type 'XXX', which is incompatible with the type 'bitmap'     DrawingForm.pro Test001_PACK\
For example, if I use BmpToRotate = vpi::pictToBitmap(PicB) to get a bitmap to rotate, the error is The expression has type '::handle', which is incompatible with the type 'bitmap'

And if I use Type BmpToRotate = PicB to get deine the bitmap, the error is The expression has type 'vpiDomains::picture', which is incompatible with the type 'bitmap'

I am looking for love (bitmaps) in all the wrong places! :?
User avatar
Thomas Linder Puls
VIP Member
Posts: 1398
Joined: 28 Feb 2000 0:01

Unread post by Thomas Linder Puls »

If your purpose is to draw the bitmap in the window somewhere, then it seems silly first to convert it from GDI to GDI+ (and then manipulate it) and then convert it again to draw it in GDI.

I think it is better to "stay" in GDI+.

Besides this you have misunderstood how a graphics and bitmaps works together. A graphics created on an image will allow you to draw in that image, rotating and translating will affect subsequent drawing operations, but will not change the image at all.

Furthermore an image created from a graphics is an empty image that just inherits settings/properties from the graphics.

So the correct way to create a rotated bitmap is first to create an empty bitmap, and then a graphics on that image, then perform rotations and translations and then finally draw the original bitmap. Than way the original bitmap will be drawn rotated and translated into the new bitmap.

Code: Select all

clauses     onPaint(_Source, _Rectangle, GDI) :-         BitMapOrig = bitmap::createFromResID(mainExe::getCurrentHinstance(), vpiDomains::application_icon, 32, 32),         % create a rotated bitmap         BitMapRot = bitmap::create(32, 32, gdiplus_native::pixelFormat32bppPARGB),         G2 = graphics::createFromImage(BitMapRot),         G2:translateTransform(16, 16),         G2:rotateTransform(30),         G2:drawImageI(BitMapOrig, -16, -16),         % Now do the drawing         HCD = GDI:getNativeGraphicContext(Release),         Graphics = graphics::createFromHDC(HCD),         Graphics:drawImageI(BitMapOrig, gdiplus::pointI(50, 50)),         Graphics:drawImageI(BitMapRot, gdiplus::pointI(150, 50)),         GDI:releaseNativeGraphicContext(HCD, Release).

It will of course be more efficient not to create the intermediate bitmap at all but to rotate and translate the window graphics instead. But it can be quite challenging to keep track of translations and rotations.

If you try this approach it will be a good idea to save the original transformation so that you can reset it back to normal between transformation steps.

By the way it is much simpler to startup and shutdown GDI+ in the main run predicate:

Code: Select all

clauses     run() :-         Token = gdiplus::startUp(),         TaskWindow = taskWindow::new(),         TaskWindow:show(),         gdiplus::shutDown(Token).
Regards Thomas Linder Puls
PDC
Harrison Pratt
VIP Member
Posts: 439
Joined: 5 Nov 2000 0:01

Unread post by Harrison Pratt »

Thomas,

Thank you for your clarification -- I greatly appreciate the time that it took you to get me started down the correct way of thinking! My initial goal was to stay in GDI+, but I became lost wandering in the forest of ancient classes (no criticism intended).

Thanks for the hint on initializing the application in the run/0 clause. That tidy approach has many uses.

I wonder if cachedBitmap might offer some performance benefits, but I'm a long way from needing to add that complexity now.

My toy GDI+ learning application is to create a little sailboat racing tactical simulator with top-views of boats on port or starboard tack and initially just show how wind shifts affect their relationship to a windward mark of the course. Images of boats on each tack (P&S) would move across the screen and have their path and rotation on the screen (compass heading) changed as the wind shifted. Of course, each boat (object) would have attributes such as maximum speed (proportional to square root of waterline length), time required to change tack, time required to resume full speed after a tack, etc. -- elaborations to be added once I understand the graphic display.

:D
Harrison Pratt
VIP Member
Posts: 439
Joined: 5 Nov 2000 0:01

Unread post by Harrison Pratt »

I don't understand how to use optional{image} in this context. The IDE generates a compiler error on this line of code:

Code: Select all

BitMapOrig = bitmap::createFromResID( mainExe::getCurrentHinstance(), vpiDomains::application_icon, 32, 32 ),
e504 The expression has type 'bitmap', which is incompatible with the type 'core::optional{image}'
User avatar
Thomas Linder Puls
VIP Member
Posts: 1398
Joined: 28 Feb 2000 0:01

Unread post by Thomas Linder Puls »

I guess you are passing BitMapOrig to some predicate that takes an optional{image} as argument.

An optional{image} is either some(<<Image>>) or none.

So that predicate should have some(BitMapOrig) as argument.
Regards Thomas Linder Puls
PDC
Harrison Pratt
VIP Member
Posts: 439
Joined: 5 Nov 2000 0:01

Unread post by Harrison Pratt »

Sorry for my ambiguity. The compiler error occured when I pasted the sample code you provided earlier into the onPaint/3 clause for my test form:

Code: Select all

        onPaint( _Source, _Rectangle, GDI):-                 /* ERROR HERE: */ BitMapOrig = bitmap::createFromResID(mainExe::getCurrentHinstance(), vpiDomains::application_icon, 32, 32),                 % create a rotated bitmap                 BitMapRot = bitmap::create(32, 32, gdiplus_native::pixelFormat32bppPARGB),                 G2 = graphics::createFromImage(BitMapRot),                 G2:translateTransform(16, 16),                 G2:rotateTransform(30),                 G2:drawImageI(BitMapOrig, -16, -16),                 % Now do the drawing                 ...
User avatar
Thomas Linder Puls
VIP Member
Posts: 1398
Joined: 28 Feb 2000 0:01

Unread post by Thomas Linder Puls »

Sorry, that is my fault. I didn't run the code in Vip 7.5 but in our current version.

In Vip 7.5 drawImageI takes an optional{image} as argument. (Which is very silly because who will ever draw none image, so in the upcoming release drawImageI takes an image (non-optional) as argument).

Anyways, in Vip 7.5 you will have to wrap the images in some to kame them optional{image}. For example like this:

Code: Select all

predicates     onPaint : window::paintResponder. clauses     onPaint(_Source, _Rectangle, GDI) :-         BitMapOrigOpt = some(bitmap::createFromResID(mainExe::getCurrentHinstance(), vpiDomains::application_icon, 32, 32)),         % create a rotated bitmap         BitMapRot = bitmap::create(32, 32, gdiplus_native::pixelFormat32bppPARGB),         G2 = graphics::createFromImage(BitMapRot),         G2:translateTransform(16, 16),         G2:rotateTransform(30),         G2:drawImageI(BitMapOrigOpt, -16, -16),         % Now do the drawing         HCD = GDI:getNativeGraphicContext(Release),         Graphics = graphics::createFromHDC(HCD),         Graphics:drawImageI(BitMapOrigOpt, gdiplus::pointI(50, 50)),         Graphics:drawImageI(some(BitMapRot), gdiplus::pointI(150, 50)),         GDI:releaseNativeGraphicContext(HCD, Release).
Regards Thomas Linder Puls
PDC
Harrison Pratt
VIP Member
Posts: 439
Joined: 5 Nov 2000 0:01

Unread post by Harrison Pratt »

Ahhh ... now I understand.

Thank you!
Post Reply