The effect is to let the user define a round "cookie" selection in an image and then "twist" it -180 to 180 degrees.
Unzip into a folder. Copy a large picture into the Project folder and rename it "sample.jpg" or update this line:
Code:
Private Const SAMPLE_PIC_FILE As String = "sample.jpg"
Run the program.
Make a circular selection by left-clicking at the center then dragging out to establish the radius.
Then you can use the Up/Down control to set a rotation angle in degrees.
After that you can click on the rotation Command button linked to the Up/Down to perform the desired rotation.
Make a new selection, or click on the Reload button.
The "work image" is scaled to fit the Form. You can click the Restore button in the caption bar to unmaximize, and then drag to resize the Form to various sizes.
That actual clipping and rotation occurs on the original image however, which can be larger or smaller than the area displayed within the Form.
The user interface isn't very refined, but should give you the idea. The scale factor in % is displayed in the caption bar to give you some idea of the scaling that is active.
The Pic object holds the original or edited image, so you can use any code you can already find here in the CodeBank for saving a bitmap-type IPicture/StdPicture to disk with or without compression. Even VB's SavePicture works, though it can only save as an uncompressed BMP image file.
Last edited by dilettante; Sep 6th, 2021 at 08:30 AM.
No, this is not a usable graphics utility program and it is not meant to be.
It demonstrates a few things you might use though:
Scaling an image to fit the Form without scrollbars. Not a substitute for variable magnification and scrolling, but this was more of an example of the other features.
Working on an image of a different actual size than the scaled size displayed.
Circular "rubberband" selection.
Use of PlgBlt() for image rotation.
Use of GDI clip regions, in this case a circular one.
Updating the bitmap held within an IPicture/StdPicture object. No need to paint to a new bitmap and wrap that as a new IPicture. IPicture was used here because its Render() method is slightly friendlier than the one exposed by the StdPicture interface.
With some changes you can also use PlgBlt() to do vertical and/or horizontal flips.
Last edited by dilettante; Sep 6th, 2021 at 12:31 PM.
Private Enum POLYFILL_MODES
ALTERNATE = 1
WINDING = 2
[_POLYFILL_LAST] = 2
End Enum
Private Declare Function CreatePolygonRgn Lib "gdi32" ( _
ByRef Points As POINTL, _
ByVal nPoints As Long, _
Optional ByVal fnPolyFillMode As POLYFILL_MODES = WINDING) As Long
Replace the subroutine:
Code:
Public Sub Rotate( _
ByVal Picture As stdole.IPicture, _
ByVal CenterX As Long, _
ByVal CenterY As Long, _
ByVal Radius As Long, _
ByVal Rotation As Double, _
ByVal ScaleFactor As Double)
'CenterX, CenterY, Radius are in scaled pixels on entry.
Dim Points(2) As POINTL
Dim I As Long
Dim RotatedPoints(2) As POINTL
Dim VertexScale As Double
Dim RegionPoints(9) As POINTL
Dim hdcScreen As Long
Dim hdcPicture As Long
Dim hbmPictureOrig As Long
Dim sbModeOrig As StretchBltModes
Dim hRegion As Long
Dim Result As Long
Dim LastDllError As Long
'Rescale then start Points() with center at (0, 0):
CenterX = Int(CenterX / ScaleFactor + 0.5)
CenterY = Int(CenterY / ScaleFactor + 0.5)
Radius = Int(Radius / ScaleFactor + 0.5)
Points(0).X = -Radius
Points(0).Y = -Radius
Points(1).X = Radius
Points(1).Y = Points(0).Y
Points(2).X = Points(0).X
Points(2).Y = Radius
'Degrees to radians:
Rotation = Rotation * PI / 180
'Adjust center to the target center and rotate Points():
For I = 0 To 2
RotatedPoints(I).X = CenterX _
+ Int(Points(I).X * Cos(Rotation) - Points(I).Y * Sin(Rotation) + 0.5)
RotatedPoints(I).Y = CenterY _
+ Int(Points(I).X * Sin(Rotation) + Points(I).Y * Cos(Rotation) + 0.5)
Next
'Adjust center to the target center in a similar manner for the star:
For I = 0 To 9
'Indent odd vertices:
If I Mod 2 Then VertexScale = Radius * 0.28 Else VertexScale = Radius
RegionPoints(I).X = CenterX _
- VertexScale * Cos(Rotation + PI * 2 * I / 5# + PI / 2)
RegionPoints(I).Y = CenterY _
- VertexScale * Sin(Rotation + PI * 2 * I / 5# + PI / 2)
Next
hdcScreen = GetDC(WIN32_NULL)
hdcPicture = CreateCompatibleDC(hdcScreen)
ReleaseDC WIN32_NULL, hdcScreen
hbmPictureOrig = SelectObject(hdcPicture, Picture.Handle)
sbModeOrig = SetStretchBltMode(hdcPicture, sbmHalftone)
hRegion = CreatePolygonRgn(RegionPoints(0), 10)
SelectClipRgn hdcPicture, hRegion
DeleteObject hRegion 'Don't need it, the hDC makes a copy.
Result = PlgBlt(hdcPicture, _
RotatedPoints(0), _
hdcPicture, _
CenterX - Radius, _
CenterY - Radius, _
Radius * 2, _
Radius * 2, _
WIN32_NULL, _
0, _
0)
LastDllError = Err.LastDllError
SelectClipRgn hdcPicture, WIN32_NULL
SetStretchBltMode hdcPicture, sbModeOrig
SelectObject hdcPicture, hbmPictureOrig
DeleteDC hdcPicture
If Result = 0 Then
Err.Raise &H8004C700, TypeName(Me), "PlgBlt error " & CStr(LastDllError)
End If
End Sub
The changes are actually pretty minor. You could vary the "indenting" factor of the odd vertices (in red above) to create a chubbier or skinnier star "cutout."