Android Question rotate a non square bitmap with transparent parts

Toni

Member
Licensed User
Longtime User
Hello,
I tried out everything I found in the forum but no code example works for me.
I flipped the bitmap of an non square and partly transparent imageview and it works fine. I've tried to translate this to
a rotating sub to do in the same manor, but find no working solution. I tried also out a DestRec with dip values thatare
explicit larger than a 45° rotated bitmap of my imageview, but at the end even when I change width and height for the
imageview the dimension stay as initial values. For rotating square bitmaps the code works fine. I know that I have to
recalculate left and top for non square bitmaps but that should be a peace of cake, even for me :rolleyes:
Any hints please? Thanks in advance!

rotating a bitmap:
Sub Gesture_onPointerDown(ptrIndex As Int, PID As Int, MotionEvent As Object)
    ' flipp image works fine
    Dim i As ImageView
    i=Sender
    Dim j As Int=i.tag  

    Dim vert As Boolean, hor As Boolean
    Dim bmp As Bitmap=i.bitmap
    Dim bmpflip As Bitmap
    bmpflip.InitializeMutable(bmp.width, bmp.height)
    Dim cv As Canvas
    cv.Initialize2(bmpflip)  
    cv.DrawColor(Colors.ARGB(0, 0, 0, 0))                                   ' 0 means fully transparent
    If bmp.Width>bmp.Height Then
        vert=True
        hor=False
    Else
        vert=False
        hor=True
    End If
    Dim DestRect As Rect
    DestRect.Initialize(0dip, 0dip, bmp.Width, bmp.height)
    cv.DrawBitmapFlipped(bmp, Null, DestRect, vert, hor) 
    i.Bitmap=bmpflip
    i.Invalidate
End Sub

Sub Gesture_onDoubleTap(X As Float, Y As Float, MotionEvent As Object)
' rotating  the bitmap works not
    Dim i As ImageView
    i=Sender
    Dim j As Int=i.tag   
    Dim bmp As Bitmap=i.bitmap
    Dim bmprot As Bitmap
    bmprot.InitializeMutable(bmp.width, bmp.height)
    Dim cv As Canvas
    cv.Initialize2(bmprot)
    cv.DrawColor(Colors.ARGB(0, 0, 0, 0))                           ' 0 means fully transparent
    Dim DestRect As Rect
    DestRect.Initialize(0dip, 0dip, bmp.Width, bmp.height)            ' also tried changing width and height, 210dip x 210dip or added a multiplyer to  width and height
    cv.DrawBitmaprotated(bmp, Null, DestRect, 90)
    i.Bitmap=bmprot                                           ' also tried setLayoutAnimated(0, i.left, i.top, new width, new height)
    i.Invalidate
end sub
 

Sagenut

Expert
Licensed User
Longtime User
What exactly is the problem?
Using Bitmap.Rotate does not make the job?
 
Upvote 0

klaus

Expert
Licensed User
Longtime User
Do you really need to draw the rotated bitmap.
Can't you not just rotate the ImageView or Panel.
Declare the views as B4XView and you can rotate those around the center.
If you need other rotations you may have a look HERE.
 
  • Like
Reactions: zed
Upvote 0

Toni

Member
Licensed User
Longtime User
@Sagenut: I'll try rotate but I think I'd tried it out before but not sure. Thanks!
@klaus: No, unfortunalty if I rotate the whole view dragging got fuzzy.
I 'll take a look to your link. Thanks!

Edit: @Sagenut: tried out bitmap.rotate. Maybe I do something wrong, but no success on non square imageview.
@klaus: rotating with javaobject works, but a following dragging routine leads also to fuzzy jumping of the view.
 
Last edited:
Upvote 0

Toni

Member
Licensed User
Longtime User
Here it comes... The rotation works for square pieces like the 3 blocks piece looks like a corner. The origin plan was to do a smooth animation of the 90° rotation. But now I'll be satisfied is it rotate 90°. If I ever finish it :cool:, it will be a mixture of tetris and tangram.

Edit: Interesting, if I shift the code to the button_click it works without any fuzzy behavior.

code owned by a button:
Private Sub btnPause_Click
'    Dim i As ImageView
'    i=Sender
    Dim j As Int            '=i.tag
    Dim w, h As Int
    j=5                        ' no5 as an example
    w=piece(j).Width
    h=piece(j).Height
    Dim bmp As Bitmap=piece(j).bitmap
    Dim bmprot As Bitmap
    bmprot.InitializeMutable(210dip, 210dip)
    Dim cv As Canvas
    cv.Initialize2(bmprot)
    cv.DrawColor(Colors.ARGB(0, 0, 0, 0))                           ' 0 means fully transparent
    Dim DestRect As Rect
    DestRect.Initialize(0dip, 0dip, 210dip, 210dip)            ' also tried changing width and height, 210dip x 210dip or added a multiplyer to  width and height
    cv.DrawBitmaprotated(bmp, Null, DestRect, 90)
    bmprot.Resize(h,w,True)
    piece(j).SetLayout(piece(j).Left,piece(j).Top,h,w)
    piece(j).Bitmap=bmprot                                           ' also tried setLayoutAnimated(0, i.left, i.top, new width, new height)
    piece(j).Invalidate
End Sub
 

Attachments

  • game.zip
    16.9 KB · Views: 13
Last edited:
Upvote 0

zed

Well-Known Member
Licensed User
Declare the views as B4XView and you can rotate those around the center.
Screenshot_20251123-090733.png
 
Upvote 0

Toni

Member
Licensed User
Longtime User
Thanks zed, but did you try it with the gesturedetector? I can't bring it to work.

Edit: I tried following code found on the internet, but my pieces disapear. What's wrong?

B4X:
Sub Gesture_onDoubleTap(X As Float, Y As Float, MotionEvent As Object)
    Dim i As B4XView
    i=Sender
    Dim j As Int=i.tag
    Dim bmp As B4XBitmap=piece(j).Bitmap
    piece(j).Bitmap=RotateBitmap(bmp, 90)
End Sub



Sub RotateBitmap (B4Xbmp As B4XBitmap, degrees As Float) As B4XBitmap
    Dim cvs As B4XCanvas
    Dim panel As B4XView = XUI.CreatePanel("")
    Dim rectTest As B4XRect
    rectTest.Initialize(0, 0, B4Xbmp.Width, B4Xbmp.Height)
    panel.Width = B4Xbmp.Width * Abs(CosD(degrees)) + B4Xbmp.Height * Abs(SinD(degrees))
    panel.Height = B4Xbmp.Width * Abs(SinD(degrees)) + B4Xbmp.Height * Abs(CosD(degrees))
    rectTest.Initialize(B4Xbmp.Height * Abs(SinD(degrees)) / 2, B4Xbmp.Width * Abs(SinD(degrees)) / 2, B4Xbmp.Width + B4Xbmp.Height * Abs(SinD(degrees)) / 2, B4Xbmp.Height + B4Xbmp.Width * Abs(SinD(degrees)) / 2)
    cvs.Initialize(panel)
    cvs.DrawBitmapRotated(B4Xbmp, rectTest, degrees)
    Dim b As B4XBitmap = cvs.CreateBitmap
    cvs.Release
    Return b
End Sub
 
Last edited:
Upvote 0

zed

Well-Known Member
Licensed User
Image rotation:
Instead of using Canvas.DrawBitmapRotated (which cropped or reduced the images), we used the Android Matrix API via JavaObject.
Matrix.postRotate(angle) applies an exact rotation.
Bitmap.createBitmap(..., matrix, True) generates a new rotated bitmap with the correct bounding box.

Sizing adjustments: After rotation, we update the width and height of the ImageView to match the new dimensions of the bitmap. This avoids the "smaller image" or "cropped image" effect.

Handling pieces of different sizes: Like in Tetris, each piece can have a different width/height ratio. At 90° or 270°, we swap the width and height to ensure the piece remains consistent on the grid.


See the screenshots

Before DoubleTap
1.png


After DoubleTap
2.png
 

Attachments

  • ProjectRotate.zip
    20.7 KB · Views: 12
Last edited:
Upvote 0

Toni

Member
Licensed User
Longtime User
Hi zed,
thank you for your patience with me. I see your screenshots and was glad that the problem seems to be solved. I'am quite sure that you solved the problem but even I installed your code several times without any changes, I've got the same problem as before. There are just three messages: that test.png is missing, BCCreator library is not used and that I should use targetSDKversion 35 instead of 34. Mmmmh, SDK36 is installed, but in the manifest is written 35 at my game example. Could that be the problem that SDK 36 (which is installed on my laptop) makes the problem?? Or any other ideas?
 
Upvote 0

Toni

Member
Licensed User
Longtime User
I just see in the SDK manager that a lot of extensions are not installed. If I search for matrix I get a message to install all extensions is recommended.
Is that true? I ask why I do not find "select all". And how can I choose all at once because that are a lot of extensions
 
Upvote 0

klaus

Expert
Licensed User
Longtime User
I tested zed's program.
It works, but i get this when double clicking on an ImageView.

Moving a view:

1763922738046.png


And then, double tabbing on it, i get this :

1763922866559.png


This shows that the width and height are not taken into account.
It seems that GestureDetection keeps the 'old' dimensions of the view.
 
Upvote 0

zed

Well-Known Member
Licensed User
1. The initial problem

In the first version of the code:

The pieces started at the bottom with a scale of 0.6.
When moved above a virtual line (Top < 340dip), they were recalculated with a scale of 0.9.
This recalculation used the initial dimensions stored in states (states.Get(“width”&j) and states.Get(“height”&j)), not the current dimensions.
Result: the pieces “inflated” at the top, then returned to their correct size at the bottom.
In addition, the condition used DipToPixels(i.Top) even though i.Top is already in dip, which skewed the threshold.


2. Problems identified

Mixing dimensions: the code used states.Get(“width”&j) and states.Get(“height”&j) (initial dimensions) to recalculate the size, which overrode the current dimensions after rotation or movement.
Dip/pixel confusion: the condition used DipToPixels(i.Top) even though i.Top is already in dip, distorting the threshold.
Conditional resizing: the scaling logic (0.6 at the bottom, 0.9 at the top) caused visual inconsistencies.


3. Changes made
a. Correct initialization of the Map
Before using states.Get(...), the Map must be initialized and filled

...:
states.Initialize
For i = 0 To 8
    states.Put("Left" & i, piece(i).Left)
    states.Put("Top" & i, piece(i).Top)
    states.Put("width" & i, piece(i).Width)
    states.Put("height" & i, piece(i).Height)
Next

b. Set a single scale

All pieces are displayed at 60% of their original size, everywhere on the screen:

...:
piece(i).SetLayoutAnimated(0, states.Get("Left"&i) + XStart(i), states.Get("Top"&i) + YStart(i), Round(BaseW(i) * 0.6), Round(BaseH(i) * 0.6))

c. Simplification of dragging

Dragging only moves the part without recalculating its size:

...:
Sub Gesture_onDrag(deltaX As Float, deltaY As Float, MotionEvent As Object)
    Dim i As ImageView = Sender
    Dim newLeft As Int = i.Left + deltaX
    Dim newTop As Int = i.Top + deltaY

    newLeft = Max(9dip, Min(newLeft, bg.Width - i.Width - 9dip))
    newTop = Max(9dip, Min(newTop, bg.Height - i.Height))

    i.SetLayoutAnimated(0, newLeft, newTop, i.Width, i.Height)
End Sub

d. Consistent rotation

When double-tapping, the bitmap is rotated and the width/height is swapped, but the fixed scale of 0.6 is always applied:

...:
Sub Gesture_onDoubleTap(X As Float, Y As Float, MotionEvent As Object)
    Dim i As ImageView = Sender
    Dim j As Int = i.Tag

    Dim bmp As B4XBitmap = i.Bitmap
    Dim rotated As B4XBitmap = RotateBitmapJO(bmp, 90)
    i.Bitmap = rotated

    Dim tmp As Int = BaseW(j)
    BaseW(j) = BaseH(j)
    BaseH(j) = tmp

    i.SetLayoutAnimated(0, i.Left, i.Top, Round(BaseW(j) * 0.6), Round(BaseH(j) * 0.6))
End Sub

e. Cleaning up other events

Gesture_onTouch no longer resizes on release (ACTION_UP).
Gesture_onFling and Gesture_onPointerDown also use the fixed scale of 0.6 for consistency.

4. Final result

All pieces start at the bottom with a reduced size (0.6).
They keep this size everywhere, even if they are moved to the top.
Rotations and flips follow the same scale.
No more “swelling” or unexpected changes in size.
The code is simpler, more stable, and more predictable.
 

Attachments

  • ProjectRotate2.zip
    20.8 KB · Views: 5
Upvote 0

Toni

Member
Licensed User
Longtime User
Wow, thank you very much for your work! A lots of hints, I have to learn! Even I'am a Long time User i was absent for 10 years. I will Look in the evening to your improvements.
 
  • Like
Reactions: zed
Upvote 0

Toni

Member
Licensed User
Longtime User
I've read your modified code just as a textfile while I'am on tour. At the end I must have 3 scales: 0.6 at the Bottom, 0.9 when moving around and 1.0 when touchevent is action_up under certain conditions. Is that impossible or do I understand you in a wrong way?
 
Upvote 0

zed

Well-Known Member
Licensed User
You can manage three distinct scale states (0.6, 0.9, 1.0) depending on the context. The idea is simply to clearly separate when each scale is applied: Scale 0.6 at startup and while the part is at the bottom. Scale 0.9 during movement (drag in progress). Scale 1.0 when you release the part (ACTION_UP) and certain conditions are met (for example, if it is correctly positioned in a target area). So yes, you can perfectly well have three scales. The key is to manage the current state (Scale(j)) and decide when to apply each value (init, drag, action_up).
However, as soon as you switch to three states (0.6, 0.9, 1.0), the visual effect becomes inconsistent. This makes sense, because every change of scale abruptly alters the size of the piece, and since the anchor is the upper left corner, you get the impression that the piece is "jumping" or inflating/deflating in an unnatural way.

0.6 at the bottom: reduced, consistent size.
0.9 while moving: the object enlarges as soon as it's picked up, which can be perceived as a zoom.
1.0 upon release: it returns to its original size, visually breaking the continuity (it's larger than when you picked it up).
Because each state is applied without a smooth transition, the eye perceives "jumps" in size.

You can keep the three states, but use SetLayoutAnimated with a duration (e.g., 200 ms) to make the transition from 0.6 to 0.9 to 1.0 smooth. Visually, it becomes a gradual zoom, therefore more natural.

In this case, you need to change the anchoring.
Example: calculate newLeft = cx - w/2 and newTop = cy - h/2 instead of keeping Left/Top.
If your goal is visual consistency, the simplest solution is to stick with two states (0.6 and 0.9). If you absolutely must have three states, it's a good idea to add a smooth animation and recalculate around the center so that the transition to 1.0 is perceived as a natural zoom, not an inconsistent expansion.

Behavior Summary
Start: All parts are set to 0.6. Drag: Smoothly zooms to 0.9, keeping the center fixed.
Release (ACTION_UP): Smoothly zooms to 1.0 if the condition is met (Top < 340dip), otherwise smoothly returns to 0.6.
Rotation: Maintains the current scale, without visual jumps.
Animated transitions (with SetLayoutAnimated and a duration).
Recalculates around the center (instead of the top left corner) to prevent the part from "jumping" when its size changes.
 

Attachments

  • Project3States.zip
    20.7 KB · Views: 1
Upvote 0
Top