Jump to content
c.haslam

GDI+: Clipping fails after rotation

Recommended Posts

c.haslam

UEZ has kindly provided me with code that rotates an image per a line: see here

Because I want to trim a photograph after rotation, I need to crop the photo. I wrote code which calls _GDIPlus_GraphicsSetClipRect() to do this, but it places spurious stuff in the corners: see 2.jpg attached.

I want only the trimmed photo to show.

The original jpeg is also attached.

The arguments to _GDIPlus_GraphicsSetClipRect() come from solving 2 simultaneous equations: if a and b are the width and height of the original photo, find x and y, the width and height of the rotated and cropped rectangle;

a = x cos(ang) + y sin(ang)

b = x sin(ang) + y cos(ang)

My solution is:

x = (b * sin(ang) - a*cos(ang)) / (sin(ang)^2 - (cos(ang)^2)

y = (b * cos(ang) - a * sin(ang)) / (cos(ang)^2 - sin(ang)^2)

To try to get a handle on the problem, I have temporarily added in several lines in Case $btn. In 2.jpg, you can see that the rectangle bounding the area to be retained is in the wrong place.

The code is:

#include <ButtonConstants.au3>
#include <GDIPlus.au3>
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>
Opt('MustDeclareVars',1)

Global Const $MK_SHIFT = 0x4
Global Const $MK_CONTROL = 0x8
Global Const $fPi = ACos(-1), $fPi2 = $fPi / 2, $fRad = 180 / $fPi

GUIRegisterMsg($WM_MOUSEWHEEL, "WM_MOUSEWHEEL")
GUIRegisterMsg($WM_PAINT,'WM_PAINT')

Global $gX0=10,$gX1=580,$gY0,$gY1,$gForm1,$glblPic,$iLW,$iLH,$iW,$iH
Global $ghCanvas,$ghImage,$ghPen,$gGraphicPic,$ghBitmap,$ghMatrix,$ghImageClone,$ghGfxClone

Global Const $kDegToRads = 3.14159/180
main()

Func main()
$gForm1 = GUICreate("Form1", 623, 601, 192, 114)
$iLW = 589
$iLH = 500
$glblPic = GUICtrlCreateLabel("", 8, 8, $iLW, $iLH)
Local $btn = GUICtrlCreateButton("Rotate", 472, 560, 65, 25)
GUISetState(@SW_SHOW)

    Local $oldY0,$oldY1,$fAngle

    _GDIPlus_Startup()

    $ghPen = _GDIPlus_PenCreate(0xFF999999,2)
    $ghImage = _GDIPlus_ImageLoadFromFile('H:\b\pergola.jpg')
    Local $h = GUICtrlGetHandle($glblPic)
    $gGraphicPic = _GDIPlus_GraphicsCreateFromHWND($h)
    $gY0 = 400
    $gY1 = 400
    $iW = _GDIPlus_ImageGetWidth($ghImage)
    $iH = _GDIPlus_ImageGetHeight($ghImage)
    $ghBitmap = _GDIPlus_BitmapCreateFromScan0($iW, $iH)
    $ghCanvas = _GDIPlus_ImageGetGraphicsContext($ghBitmap)
    $ghMatrix = _GDIPlus_MatrixCreate()
    ; Loop until the user exits.
    While True
        If $gY0<>$oldY0 Or $gY1<>$oldY1 Then
            Paint()
            $oldY0 = $gY0
            $oldY1 = $gY1
        Else
            Switch GUIGetMsg()
                Case $GUI_EVENT_CLOSE
                    ExitLoop
                Case $btn
                    $ghImageClone=_GDIPlus_BitmapCreateFromScan0($iW, $iH)
                    $ghGfxClone=_GDIPlus_ImageGetGraphicsContext($ghImageClone)
                    _GDIPlus_MatrixTranslate($ghMatrix, $iW/2, $iH/2)
$gY1 = 300  ; temp
                    Local $ang = -Angle($gY1-$gY0, $gX1-$gX0)
                    _GDIPlus_MatrixRotate($ghMatrix, $ang)
;~                    _GDIPlus_MatrixRotate($ghMatrix, -Angle($gY1-$gY0, $gX1-$gX0))
                    _GDIPlus_MatrixTranslate($ghMatrix, -$iW/2, -$iH/2)
                    _GDIPlus_GraphicsSetTransform($ghGfxClone, $ghMatrix)
                    _GDIPlus_GraphicsDrawImageRect($ghGfxClone,$ghImage,0,0,$iW,$iH)
                    Local $angRads = $ang*$kDegToRads
                    Local $sinAng = Sin($angRads)
                    Local $cosAng = Cos($angRads)
                    Local $wid = ($iLH*$sinAng-$iLW*$cosAng)/($sinAng^2-$cosAng^2)
                    Local $left = ($iLW-$wid)/2
                    Local $ht = ($iLH*$cosAng-$iLW*$sinAng)/($cosAng^2-$sinAng^2)
                    Local $top = ($iLH-$ht)/2
 _GDIPlus_GraphicsDrawRect($ghGfxClone,$left,$top,$wid,$ht,$ghPen)  ; temp
                    _GDIPlus_GraphicsSetClipRect($ghGfxClone,$left,$top,$wid,$ht,0)
                   _GDIPlus_GraphicsDispose($ghGfxClone)
                    $ghGfxClone=_GDIPlus_ImageGetGraphicsContext($ghImage)
                    _GDIPlus_GraphicsClear($ghImage)
                    _GDIPlus_GraphicsDrawImageRect($ghGfxClone,$ghImageClone,0,0,$iW,$iH)
                    _GDIPlus_GraphicsDispose($ghGfxClone)
                    _GDIPlus_ImageDispose($ghImageClone)
                    $gy0 = 400
                    $gy1 = 400
                    Paint()
                    _GDIPlus_ImageSaveToFile($ghImage,'H:\b\2.jpg') ; added - is rotated, with same size as original file
            EndSwitch
        EndIf
    WEnd

    ; Clean up resources
    _GDIPlus_MatrixDispose($ghMatrix)
    _GDIPlus_ImageDispose($ghCanvas)
    _GDIPlus_ImageDispose($ghImage)
    _GDIPlus_ImageDispose($ghBitmap)
    _GDIPlus_PenDispose($ghPen)
    _GDIPlus_GraphicsDispose($gGraphicPic)
    _GDIPlus_Shutdown()
EndFunc

Func WM_MOUSEWHEEL($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam, $lParam
    Local Const $kYmax=$iLH-1,$kDelta=2
    Local $vec = GUIGetCursorInfo($gForm1)
    If $vec[4] = $vec[4]=$glblPic Then
        Local $iDelta = BitShift($wParam, 16)   ; positive = up
        Local $iKeys = _WinAPI_LoWord($wParam)
        If BitAND($iKeys,$MK_CONTROL)=$MK_CONTROL Then
            If BitAND($iKeys,$MK_SHIFT)=$MK_SHIFT Then
                ; do nothing
            Else
                If $iDelta > 0 And $gY0>3 Then $gY0 -= $kDelta
                If $iDelta < 0 And $gY0<$kYmax Then $gY0 += $kDelta
            EndIf
        Else
            If BitAND($iKeys,$MK_SHIFT)=$MK_SHIFT Then
                If $iDelta > 0 And $gY1>3 Then $gY1 -= $kDelta
                If $iDelta < 0  And $gY1<$kYmax Then $gY1 += $kDelta
            Else
                If $iDelta > 0 And $gY0>3 Then $gY0 -= $kDelta
                If $iDelta < 0 And $gY0<$kYmax Then $gY0 += $kDelta
                If $iDelta > 0 And $gY1>3 Then $gY1 -= $kDelta
                If $iDelta < 0 And $gY1<$kYmax Then $gY1 += $kDelta
            EndIf
        EndIf
    EndIf
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_MOUSEWHEEL

Func Paint()
    _GDIPlus_GraphicsClear($ghCanvas,0)
    _GDIPlus_GraphicsDrawImageRect($ghCanvas, $ghImage, 0,0, $iLW, $iLH)
    _GDIPlus_GraphicsDrawLine($ghCanvas, $gX0, $gY0, $gX1, $gY1, $ghPen)
    _GDIPlus_GraphicsDrawImageRect($gGraphicPic, $ghBitmap, 0, 0, $iW, $iH)
EndFunc

Func WM_PAINT()
    _WinAPI_RedrawWindow($gForm1, 0, 0, $RDW_UPDATENOW)
    Paint()
    _WinAPI_RedrawWindow($gForm1, 0, 0, $RDW_VALIDATE)
EndFunc

Func Angle($y, $x) ;return value is in degree
    Local Const $fPi = ACos(-1), $fPi2 = ACos(-1) / 2, $fRad = 180 / $fPi
    Switch True
        Case ($x > 0)
            Return ATan($y / $x) * $fRad
        Case ($x < 0 And $y >= 0)
            Return ATan($y / $x + $fPi) * $fRad
        Case ($x < 0 And $y < 0)
            Return ATan($y / $x - $fPi) * $fRad
        Case ($x = 0 And $y > 0)
            Return $fPi2 * $fRad
        Case ($x = 0 And $y < 0)
            Return -$fPi2 * $fRad
        Case ($x = 0 And $y = 0)
            Return 0
    EndSwitch
EndFunc

I also don't understand how the rotated picture gets displayed. There are graphics objects, graphics contexts and PDI+ bitmaps. How are they related?

Help would be much appreciated.

pergola.jpg

2.jpg

Edited by c.haslam

Spoiler

CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard

 

Share this post


Link to post
Share on other sites
c.haslam

Thank you for the link. I have already studied that one carefully. It didn't help, perhaps because I don't know JS.

I think my basic problem may be not understanding how GD+ works with graphics objects, graphics contexts and PDI+ bitmaps. MSDN hasn't helped either.

Edited by c.haslam

Spoiler

CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard

 

Share this post


Link to post
Share on other sites
UEZ

You mean something like this here?

#include <ButtonConstants.au3>
#include <GDIPlus.au3>
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>
Opt('MustDeclareVars',1)

Global Const $MK_SHIFT = 0x4
Global Const $MK_CONTROL = 0x8
Global Const $fPi = ACos(-1), $fPi2 = $fPi / 2, $fRad = 180 / $fPi

GUIRegisterMsg($WM_MOUSEWHEEL, "WM_MOUSEWHEEL")
GUIRegisterMsg($WM_PAINT,'WM_PAINT')

Global $gX0,$gX1,$gY0,$gY1,$gForm1,$glblPic,$iW,$iH
Global $ghCanvas,$ghImage,$ghPen,$gGraphicPic,$ghBitmap,$ghMatrix,$ghImageClone,$ghGfxClone,$ghImageCropped
main()

Func main()
    Local $oldY0,$oldY1,$fAngle

    _GDIPlus_Startup()

    $ghPen = _GDIPlus_PenCreate(0xFF000000,2)
    $ghImage = _GDIPlus_ImageLoadFromFile('pergola.jpg')
    $iW = _GDIPlus_ImageGetWidth($ghImage)
    $iH = _GDIPlus_ImageGetHeight($ghImage)
    $gX0=10
    $gX1=$iW-10

    $gForm1 = GUICreate("Form1", $iW + 16, $iH + 50)
    $glblPic = GUICtrlCreateLabel("", 8, 8, $iW, $iH)
    Local $btn = GUICtrlCreateButton("Rotate", $iW - 100, $iH + 20, 65, 25)
    GUISetState(@SW_SHOW)

    Local $h = GUICtrlGetHandle($glblPic)
    $gGraphicPic = _GDIPlus_GraphicsCreateFromHWND($h)
    $gY0 = $iH / 2 + 100
    $gY1 = $iH / 2 - 90

    $ghBitmap = _GDIPlus_BitmapCreateFromScan0($iW, $iH)
    $ghCanvas = _GDIPlus_ImageGetGraphicsContext($ghBitmap)
    $ghMatrix = _GDIPlus_MatrixCreate()
    ; Loop until the user exits.
    While True
        If $gY0<>$oldY0 Or $gY1<>$oldY1 Then
            Paint()
            $oldY0 = $gY0
            $oldY1 = $gY1
        Else
            Switch GUIGetMsg()
                Case $GUI_EVENT_CLOSE
                    ExitLoop
                Case $btn
                    $ghImageClone=_GDIPlus_BitmapCreateFromScan0($iW, $iH)
                    $ghGfxClone=_GDIPlus_ImageGetGraphicsContext($ghImageClone)
                    _GDIPlus_MatrixTranslate($ghMatrix, $iW/2, $iH/2)
                    _GDIPlus_MatrixRotate($ghMatrix, -Angle($gY1-$gY0, $gX1-$gX0))
                    _GDIPlus_MatrixTranslate($ghMatrix, -$iW/2, -$iH/2)
                    _GDIPlus_GraphicsSetTransform($ghGfxClone, $ghMatrix)
                    _GDIPlus_GraphicsDrawImageRect($ghGfxClone,$ghImage,0,0,$iW,$iH)
                    _GDIPlus_GraphicsDispose($ghGfxClone)
                    $ghGfxClone=_GDIPlus_ImageGetGraphicsContext($ghImage)
                    _GDIPlus_GraphicsClear($ghGfxClone)
                    _GDIPlus_GraphicsClear($ghImage)
                    _GDIPlus_GraphicsDrawImageRect($ghGfxClone,$ghImageClone,0,0,$iW,$iH)
                    _GDIPlus_GraphicsDispose($ghGfxClone)
                    $gY0 = $iH / 2
                    $gY1 = $iH / 2
                    Paint()
                    $ghImageCropped=_GDIPlus_BitmapCloneArea($ghImageClone,120,66,745,403)
                    _GDIPlus_ImageDispose($ghImageClone)
                    _GDIPlus_ImageSaveToFile($ghImageCropped, @ScriptDir & "\pergola_RnC.jpg")
                    _GDIPlus_ImageDispose($ghImageCropped)
                    ShellExecute(@ScriptDir & "\pergola_RnC.jpg")
            EndSwitch
        EndIf
    WEnd

    ; Clean up resources
    _GDIPlus_MatrixDispose($ghMatrix)
    _GDIPlus_ImageDispose($ghCanvas)
    _GDIPlus_ImageDispose($ghImage)
    _GDIPlus_ImageDispose($ghBitmap)
    _GDIPlus_PenDispose($ghPen)
    _GDIPlus_GraphicsDispose($gGraphicPic)
    _GDIPlus_Shutdown()
EndFunc

Func WM_MOUSEWHEEL($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam, $lParam
    Local Const $kYmax=$iH-1,$kDelta=2
    Local $vec = GUIGetCursorInfo($gForm1)
    If $vec[4] = $vec[4]=$glblPic Then
        Local $iDelta = BitShift($wParam, 16)   ; positive = up
        Local $iKeys = _WinAPI_LoWord($wParam)
        If BitAND($iKeys,$MK_CONTROL)=$MK_CONTROL Then
            If BitAND($iKeys,$MK_SHIFT)=$MK_SHIFT Then
                ; do nothing
            Else
                If $iDelta > 0 And $gY0>3 Then $gY0 -= $kDelta
                If $iDelta < 0 And $gY0<$kYmax Then $gY0 += $kDelta
            EndIf
        Else
            If BitAND($iKeys,$MK_SHIFT)=$MK_SHIFT Then
                If $iDelta > 0 And $gY1>3 Then $gY1 -= $kDelta
                If $iDelta < 0  And $gY1<$kYmax Then $gY1 += $kDelta
            Else
                If $iDelta > 0 And $gY0>3 Then $gY0 -= $kDelta
                If $iDelta < 0 And $gY0<$kYmax Then $gY0 += $kDelta
                If $iDelta > 0 And $gY1>3 Then $gY1 -= $kDelta
                If $iDelta < 0 And $gY1<$kYmax Then $gY1 += $kDelta
            EndIf
        EndIf
    EndIf
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_MOUSEWHEEL

Func Paint()
    _GDIPlus_GraphicsClear($ghCanvas,0)
    _GDIPlus_GraphicsDrawImageRect($ghCanvas, $ghImage, 0,0, $iW, $iH)
    _GDIPlus_GraphicsDrawLine($ghCanvas, $gX0, $gY0, $gX1, $gY1, $ghPen)
    _GDIPlus_GraphicsDrawImageRect($gGraphicPic, $ghBitmap, 0, 0, $iW, $iH)
EndFunc

Func WM_PAINT()
    _WinAPI_RedrawWindow($gForm1, 0, 0, $RDW_UPDATENOW)
    Paint()
    _WinAPI_RedrawWindow($gForm1, 0, 0, $RDW_VALIDATE)
EndFunc

Func Angle($y, $x) ;return value is in degree
    Local Const $fPi = ACos(-1), $fPi2 = ACos(-1) / 2, $fRad = 180 / $fPi
    Switch $x
        Case ($x > 0)
            Return ATan($y / $x) * $fRad
        Case ($x < 0 And $y >= 0)
            Return ATan($y / $x + $fPi) * $fRad
        Case ($x < 0 And $y < 0)
            Return ATan($y / $x - $fPi) * $fRad
        Case ($x = 0 And $y > 0)
            Return $fPi2 * $fRad
        Case ($x = 0 And $y < 0)
            Return -$fPi2 * $fRad
        Case ($x = 0 And $y = 0)
            Return 0
    EndSwitch
EndFunc

I need to think about an automatic crop solution...

  • Like 1

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites
c.haslam

UEZ, thanks.

Unfortunately, pergola.jpg shows full screen, because your code makes $lblPiC the size of the jpeg, which is 2557x1374. The Rotate button is way of-screen.

I look forward to the result of your endeavors, I won't have time to visit the forum for a few days.

Edited by c.haslam
fixed typo

Spoiler

CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard

 

Share this post


Link to post
Share on other sites
c.haslam

UEZ,

I have purposely rotated by much more than what happens in real life: in this sample, the camera was held about 10 degrees off level; in my experience, 3 degrees is as much off as I have seen. It does, however, show that I am  looking at the problem wrongly geometrically and algebraically!

I need to do some more head-scratching to figure out what is going on.

My original idea was to improve on the way Irfanview de-skews a photo: it has the user enter the angle as a number; I would like to get a better result by the user rotating a line. Here is what Irfanview does with 10 degree rotation.

Irfanview's result is too small to be useful -- another reason why I have undertaken this project!pergola IV change size.jpg

 

pergola IV change size.jpgpergola IV keep size.jpg

Neither is what I want to do. I have been trying to do the "change .. size' option, but I now realize that I need to do IV's "keep ... size" option, then crop to get rid of the 4 corner triangles, by cropping.

Hope this makes sense.

(Onlt the first 2 images in this post apply. I can't delete the third, erroneous, one.)

pergola IV keep size.jpg

Edited by c.haslam

Spoiler

CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard

 

Share this post


Link to post
Share on other sites
UEZ

I would do it this way:

Create a large preview window of the image with a slider from -360 to 360 degrees which the user can use to rotate the image

Have a horizontal movable line over the image to have an orientation

Use mouse wheel to zoom the image

A checkbox with the open to keep original image as background


Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites
c.haslam

I don't see a need to rotate the image more than about 10 degrees. For the Photos I take, there is some feature that should be horizontal: in the case of the image I am testing with, it is the edge of the pool. More typically, the feature is near the bottom of the picture.

I have been thinking about the image Irfanview produces when Keep the original image/canvas size is checked.

I would be happy if a way can be found to replace the erroneous corner triangles with black (or the background colour AutoIt uses in a dialog box).

Then the script would then show a crop rectangle on the image.

This rectangle would initially have as its (left, bottom) the mid point of the hypotenuse of the lower left triangle, and as its (top, right) the mid point of the hypotenuse of the upper right triangle. (This is based on the image I used in Irfanview, i.e. landscape with clockwise rotation).

The script would allow the user to change the size of the rectangle, by dragging the edges, constrained by the corner triangles.

The user would then click on a Crop button to define the cropped image.

Does this make sense?


Spoiler

CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard

 

Share this post


Link to post
Share on other sites
UEZ

I don't get it with the corner issue you have. When you rotate the image then you will get automatically the black triangle corners.

Or do you mean after rotation to zoom in the image that you cannot see any black triangle corner?


Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites
c.haslam

If you look at the second image in Post #1, at the top right corner, you can see lattice on an angle. and part of the top of the fence: in a triangle. This triangle should be solid black. I also see triangles in the other 3 corners. They too should be black.

 


Spoiler

CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard

 

Share this post


Link to post
Share on other sites
UEZ

The code in post#4 is exactly producing the image with the black corners! What you have to do is to remove the cropping (zoom in to avoid the black corners).


Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites
c.haslam

It occurs to me that whether the corners are black or bits of the un-rotated image are irrelevant, because they will be cropped out.

So the question is: how to crop the triangles?

SlackerAl, in Post #2, gives a link to a thread on stackoverflow. The  post in that thread posted Feb 8 '14 at 21:17 contains code that apparently crops, yielding maximum area:

CropRotatedDimensionsForMaxArea[{w_, h_}, alpha_] := 
With[
  {phi = Abs@Mod[alpha, Pi, -Pi/2]},
  Which[
   w == h, {w,h} Csc[phi + Pi/4]/Sqrt[2],
   w > h, 
     If[ Cos[2 phi]^2 < 1 - (h/w)^2, 
       h/2 {Csc[phi], Sec[phi]}, 
       Sec[2 phi] {w Cos[phi] - h Sin[phi], h Cos[phi] - w Sin[phi]}],
   w < h, 
     If[ Cos[2 phi]^2 < 1 - (w/h)^2, 
       w/2 {Sec[phi], Csc[phi]}, 
       Sec[2 phi] {w Cos[phi] - h Sin[phi], h Cos[phi] - w Sin[phi]}]
  ]
]

I don't understand the code. Perhaps someone would help me by translating this into AutoIt code. Or perhaps other code in that thread would work.

 


Spoiler

CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard

 

Share this post


Link to post
Share on other sites
c.haslam

In Post 4,  I see _GDIPlus_GraphicsClear($ghImage) but the Help says that the parameter should be a graphics object. $ghImage is an image object.

Is this why the corner triangles are not black?

I have made some progress on auto-cropping, but have not tested it yet.

 
Edited by c.haslam

Spoiler

CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard

 

Share this post


Link to post
Share on other sites
UEZ
3 hours ago, c.haslam said:

In Post 4,  I see _GDIPlus_GraphicsClear($ghImage) but the Help says that the parameter should be a graphics object. $ghImage is an image object.

Is this why the corner triangles are not black?

I have made some progress on auto-cropping, but have not tested it yet.

 

You are right, this line is obsolete and can be deleted. The line above (_GDIPlus_GraphicsClear($ghGfxClone)) clears the canvas so that the cornes get black. If you comment the line out you will see the difference.


Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites
SlackerAl
On 10/29/2017 at 6:48 PM, c.haslam said:

Post #2: I don't understand the code. Perhaps someone would help me by translating this into AutoIt code. Or perhaps other code in that thread would work.

 

I'm sorry, but I do not have time to look at this for some time, perhaps someone else can help? If not I will try to get to it, but it may be a few weeks.


Problem solving step 1: Write a simple, self-contained, running, replicator of your problem.

Share this post


Link to post
Share on other sites
c.haslam

SlackerAl,

I am not sure, but I do think, that translating the code in Post #2 may no longer be needed.

My current approach is:

  • Determine the formulae for the corners of the rotated image
  • Determine the equations for each of the sides of the rotated image
  • Determine the points where the sides of the rotated image intercept the sides of the frame (un-rotated image)
  • Crop the rotated image to be within horizontal and vertical lines drawn through these points, with the lines chosen to maximize the cropped image.

This is the outline of the approach. The devil is in the details! I have this working on paper (with manually drawn diagram/graph). I am still battling which intercept points between sides of the frame and sides of the rotated image are the correct pairs. I think I know, but getting the signs in the equations right is taking time. I am making stupid errors!


Spoiler

CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard

 

Share this post


Link to post
Share on other sites
c.haslam

My code, based on UEZ's code, is below. It works!

#include <ButtonConstants.au3>
#include <GDIPlus.au3>
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>
Opt('MustDeclareVars',1)

GUIRegisterMsg($WM_MOUSEWHEEL, "WM_MOUSEWHEEL")
GUIRegisterMsg($WM_PAINT,'WM_PAINT')

Global $gX0=10,$gX1=580,$gY0,$gY1,$gForm1,$glblPic,$gCtlWid,$gCtlHt,$imgWid,$imgHt
Global $ghCanvas,$ghImage,$ghPen,$gGraphicPic,$ghBitmap

main()

Func main()
$gCtlWid = 893
$gCtlHt = 480
$gForm1 = GUICreate("Form1", $gCtlWid+50, 601, 192, 114)
$glblPic = GUICtrlCreateLabel("", 8, 8, $gCtlWid, $gCtlHt)
Local $btnRotate = GUICtrlCreateButton("Rotate", 472, 560, 65, 25)
Local $btnExit = GUICtrlCreateButton("Exit",600,560)
GUISetState(@SW_SHOW)

    Local $oldY0,$oldY1

    _GDIPlus_Startup()

    $ghPen = _GDIPlus_PenCreate(0xFF999999,2)
    $ghImage = _GDIPlus_ImageLoadFromFile('H:\b\pergola.jpg')
    Local $hnd = GUICtrlGetHandle($glblPic)
    $gGraphicPic = _GDIPlus_GraphicsCreateFromHWND($hnd)
    $gY0 = 400
    $gY1 = 400
    $imgWid = _GDIPlus_ImageGetWidth($ghImage)
    $imgHt = _GDIPlus_ImageGetHeight($ghImage)
    $ghBitmap = _GDIPlus_BitmapCreateFromScan0($imgWid, $imgHt)
    $ghCanvas = _GDIPlus_ImageGetGraphicsContext($ghBitmap)
    ; Loop until the user exits.
    While True
        If $gY0<>$oldY0 Or $gY1<>$oldY1 Then
            Paint()
            $oldY0 = $gY0
            $oldY1 = $gY1
        Else
            Switch GUIGetMsg()
                Case $GUI_EVENT_CLOSE,$btnExit
                    ExitLoop
                Case $btnRotate
                    Local $hImageClone=_GDIPlus_BitmapCreateFromScan0($imgWid, $imgHt)
                    Local $hGfxClone=_GDIPlus_ImageGetGraphicsContext($hImageClone)
                    ; rotate about centre
                    Local $gMatrix = _GDIPlus_MatrixCreate()
                    _GDIPlus_MatrixTranslate($gMatrix, $imgWid/2, $imgHt/2)
                    Local $deg = Angle($gY1-$gY0, $gX1-$gX0)
                    _GDIPlus_MatrixRotate($gMatrix, -$deg)
                    _GDIPlus_MatrixTranslate($gMatrix, -$imgWid/2, -$imgHt/2)
                    _GDIPlus_GraphicsSetTransform($hGfxClone, $gMatrix)

                    _GDIPlus_GraphicsDrawImageRect($hGfxClone,$ghImage,0,0,$imgWid,$imgHt)
                    _GDIPlus_GraphicsDispose($hGfxClone)

                    $hGfxClone=_GDIPlus_ImageGetGraphicsContext($ghImage)
                    _GDIPlus_GraphicsClear($hGfxClone)
                    ; See deskew notes document
                    Local $ang = Abs($deg*3.14159/180)
                    Local $nsrcX = ($imgHt/2 - $imgWid/2*sin($ang) - $imgHt/2*cos($ang))*tan($ang) _
                        - $imgWid/2*cos($ang) + $imgHt/2*sin($ang) + $imgWid/2
                    Local $nsrcY = ($imgWid/2 + $imgWid/2*Cos($ang) - $imgHt/2*Sin($ang))*Tan($ang) _
                        - $imgWid/2*Sin($ang) - $imgHt/2*Cos($ang) + $imgHt/2
                    Local $nSrcWidth = $imgWid - 2*$nSrcX
                    Local $nSrcHeight = $imgHt - 2*$nSrcY
                    ; crop
                    _GDIPlus_GraphicsDrawImageRectRect($hGfxClone,$hImageClone, _
                        $nSrcX,$nSrcY,$nSrcWidth,$nSrcHeight,0,0,$nSrcWidth,$nSrcHeight)

                    GUICtrlDelete($glblPic)
                    _GDIPlus_GraphicsDispose($gGraphicPic)
                    $glblPic = GUICtrlCreateLabel("", 8, 8, $gCtlWid*$nSrcWidth/$imgWid,$gCtlHt*$nSrcHeight/$imgHt)
                    Local $hnd = GUICtrlGetHandle($glblPic)
                    $gGraphicPic = _GDIPlus_GraphicsCreateFromHWND($hnd)
                    _GDIPlus_ImageDispose($hImageClone)
                    $gy0 = 400
                    $gy1 = 400
                    Paint()
                    Local $hImageClone2 = _GDIPlus_BitmapCloneArea($ghImage,0,0,$nSrcWidth,$nSrcHeight)
                    _GDIPlus_ImageSaveToFile($hImageClone2,'H:\b\1.jpg')
                    _GDIPlus_ImageDispose($hImageClone2)
           EndSwitch
        EndIf
    WEnd

    ; Clean up resources
    _GDIPlus_MatrixDispose($gMatrix)
    _GDIPlus_ImageDispose($ghCanvas)
    _GDIPlus_ImageDispose($ghImage)
    _GDIPlus_ImageDispose($ghBitmap)
    _GDIPlus_PenDispose($ghPen)
    _GDIPlus_GraphicsDispose($gGraphicPic)
    _GDIPlus_Shutdown()
EndFunc

Func WM_MOUSEWHEEL($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam, $lParam
    Local Const $MK_SHIFT=0x4,$MK_CONTROL= 0x8
    Local Const $kYmax=$gCtlHt-1,$kDelta=2

    Local $vec = GUIGetCursorInfo($gForm1)
    If $vec[4] = $vec[4]=$glblPic Then
        Local $iDelta = BitShift($wParam, 16)   ; positive = up
        Local $iKeys = _WinAPI_LoWord($wParam)
        If BitAND($iKeys,$MK_CONTROL)=$MK_CONTROL Then
            If BitAND($iKeys,$MK_SHIFT)=$MK_SHIFT Then
                ; do nothing
            Else
                If $iDelta > 0 And $gY0>3 Then $gY0 -= $kDelta
                If $iDelta < 0 And $gY0<$kYmax Then $gY0 += $kDelta
            EndIf
        Else
            If BitAND($iKeys,$MK_SHIFT)=$MK_SHIFT Then
                If $iDelta > 0 And $gY1>3 Then $gY1 -= $kDelta
                If $iDelta < 0  And $gY1<$kYmax Then $gY1 += $kDelta
            Else
                If $iDelta > 0 And $gY0>3 Then $gY0 -= $kDelta
                If $iDelta < 0 And $gY0<$kYmax Then $gY0 += $kDelta
                If $iDelta > 0 And $gY1>3 Then $gY1 -= $kDelta
                If $iDelta < 0 And $gY1<$kYmax Then $gY1 += $kDelta
            EndIf
        EndIf
    EndIf
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_MOUSEWHEEL

Func Paint()
    _GDIPlus_GraphicsClear($ghCanvas,0)
    _GDIPlus_GraphicsDrawImageRect($ghCanvas, $ghImage, 0,0, $gCtlWid, $gCtlHt)
    _GDIPlus_GraphicsDrawLine($ghCanvas, $gX0, $gY0, $gX1, $gY1, $ghPen)
    _GDIPlus_GraphicsDrawImageRect($gGraphicPic, $ghBitmap, 0, 0, $imgWid, $imgHt)
EndFunc

Func WM_PAINT()
    _WinAPI_RedrawWindow($gForm1, 0, 0, $RDW_UPDATENOW)
    Paint()
    _WinAPI_RedrawWindow($gForm1, 0, 0, $RDW_VALIDATE)
EndFunc

Func Angle($y, $x) ;return value is in degree
    Local Const $fPi = ACos(-1), $fPi2 = ACos(-1) / 2, $fRad = 180 / $fPi
    Switch True
        Case ($x > 0)
            Return ATan($y / $x) * $fRad
        Case ($x < 0 And $y >= 0)
            Return ATan($y / $x + $fPi) * $fRad
        Case ($x < 0 And $y < 0)
            Return ATan($y / $x - $fPi) * $fRad
        Case ($x = 0 And $y > 0)
            Return $fPi2 * $fRad
        Case ($x = 0 And $y < 0)
            Return -$fPi2 * $fRad
        Case ($x = 0 And $y = 0)
            Return 0
    EndSwitch
EndFunc

I am attaching my derivation of the formulas used for cropping. Thank you to all who have posted GDI+ stuff on the forum.

I still do not fully understand how graphics objects / graphic contexts work, so helpful hints would be appreciated!

Suggestions for improvements are most welcome.

 

Deskew notes.pdf

Edited by c.haslam
Update Deskew notes.pdf

Spoiler

CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard

 

Share this post


Link to post
Share on other sites
UEZ

@c.haslam now I got your point with the edges - probably my fault because of lack of language knowledge.

But I assumed already in post#11 what you want:

On 10/26/2017 at 2:51 PM, UEZ said:

What you have to do is to remove the cropping (zoom in to avoid the black corners).

Anyhow, good work: :thumbsup:


Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites
UEZ
12 hours ago, c.haslam said:

Suggestions for improvements are most welcome.

I wouldn't say it is an improvement rather another (brute-force) way to get the best crop area (the aspect ratio of the original image is kept!) for the image after rotation whereas the corners are black (0x00000000).

#include <GDIPlus.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
Opt('MustDeclareVars',1)

Global Const $MK_SHIFT = 0x4
Global Const $MK_CONTROL = 0x8
Global Const $fPi = ACos(-1), $fPi2 = $fPi / 2, $fRad = 180 / $fPi

GUIRegisterMsg($WM_MOUSEWHEEL, "WM_MOUSEWHEEL")
GUIRegisterMsg($WM_PAINT,'WM_PAINT')

Global $gX0,$gX1,$gY0,$gY1,$gForm1,$glblPic,$iW,$iH
Global $ghCanvas,$ghImage,$ghPen,$gGraphicPic,$ghBitmap,$ghMatrix,$ghImageClone,$ghGfxClone,$ghImageCropped
main()

Func main()
    Local $oldY0,$oldY1,$fAngle,$aPos

    _GDIPlus_Startup()

    $ghPen = _GDIPlus_PenCreate(0xFFFF0000,2)
    $ghImage = _GDIPlus_ImageLoadFromFile('pergola.jpg')
    $iW = _GDIPlus_ImageGetWidth($ghImage)
    $iH = _GDIPlus_ImageGetHeight($ghImage)
    $gX0=10
    $gX1=$iW-10

    $gForm1 = GUICreate("Form1", $iW + 16, $iH + 50)
    $glblPic = GUICtrlCreateLabel("", 8, 8, $iW, $iH)
    Local $btn = GUICtrlCreateButton("Rotate", $iW - 100, $iH + 20, 65, 25)
    GUISetState(@SW_SHOW)

    Local $h = GUICtrlGetHandle($glblPic)
    $gGraphicPic = _GDIPlus_GraphicsCreateFromHWND($h)
    $gY0 = $iH / 2 + 100
    $gY1 = $iH / 2 - 90

    $ghBitmap = _GDIPlus_BitmapCreateFromScan0($iW, $iH)
    $ghCanvas = _GDIPlus_ImageGetGraphicsContext($ghBitmap)
    $ghMatrix = _GDIPlus_MatrixCreate()
    ; Loop until the user exits.
    While True
        If $gY0<>$oldY0 Or $gY1<>$oldY1 Then
            Paint()
            $oldY0 = $gY0
            $oldY1 = $gY1
        Else
            Switch GUIGetMsg()
                Case $GUI_EVENT_CLOSE
                    ExitLoop
                Case $btn
                    $ghImageClone=_GDIPlus_BitmapCreateFromScan0($iW, $iH)
                    $ghGfxClone=_GDIPlus_ImageGetGraphicsContext($ghImageClone)
                    _GDIPlus_MatrixTranslate($ghMatrix, $iW/2, $iH/2)
                    _GDIPlus_MatrixRotate($ghMatrix, -Angle($gY1-$gY0, $gX1-$gX0))
                    _GDIPlus_MatrixTranslate($ghMatrix, -$iW/2, -$iH/2)
                    _GDIPlus_GraphicsSetTransform($ghGfxClone, $ghMatrix)
                    _GDIPlus_GraphicsDrawImageRect($ghGfxClone,$ghImage,0,0,$iW,$iH)
                    _GDIPlus_GraphicsDispose($ghGfxClone)
                    $ghGfxClone=_GDIPlus_ImageGetGraphicsContext($ghImage)
                    _GDIPlus_GraphicsClear($ghGfxClone)
                    _GDIPlus_GraphicsClear($ghImage)
                    _GDIPlus_GraphicsDrawImageRect($ghGfxClone,$ghImageClone,0,0,$iW,$iH)
                    _GDIPlus_GraphicsDispose($ghGfxClone)
                    $gY0 = $iH / 2
                    $gY1 = $iH / 2
                    Paint()
                    $aPos = GetBestCropArea($ghImageClone)
                    $ghImageCropped=_GDIPlus_BitmapCloneArea($ghImageClone,$aPos[0],$aPos[1],$aPos[2],$aPos[3])
                    _GDIPlus_ImageDispose($ghImageClone)
                    _GDIPlus_ImageSaveToFile($ghImageCropped, @ScriptDir & "\pergola_RnC.jpg")
                    _GDIPlus_ImageDispose($ghImageCropped)
                    ShellExecute(@ScriptDir & "\pergola_RnC.jpg")
            EndSwitch
        EndIf
    WEnd

    ; Clean up resources
    _GDIPlus_MatrixDispose($ghMatrix)
    _GDIPlus_ImageDispose($ghCanvas)
    _GDIPlus_ImageDispose($ghImage)
    _GDIPlus_ImageDispose($ghBitmap)
    _GDIPlus_PenDispose($ghPen)
    _GDIPlus_GraphicsDispose($gGraphicPic)
    _GDIPlus_Shutdown()
EndFunc

Func WM_MOUSEWHEEL($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam, $lParam
    Local Const $kYmax=$iH-1,$kDelta=2
    Local $vec = GUIGetCursorInfo($gForm1)
    If $vec[4] = $vec[4]=$glblPic Then
        Local $iDelta = BitShift($wParam, 16)   ; positive = up
        Local $iKeys = _WinAPI_LoWord($wParam)
        If BitAND($iKeys,$MK_CONTROL)=$MK_CONTROL Then
            If BitAND($iKeys,$MK_SHIFT)=$MK_SHIFT Then
                ; do nothing
            Else
                If $iDelta > 0 And $gY0>3 Then $gY0 -= $kDelta
                If $iDelta < 0 And $gY0<$kYmax Then $gY0 += $kDelta
            EndIf
        Else
            If BitAND($iKeys,$MK_SHIFT)=$MK_SHIFT Then
                If $iDelta > 0 And $gY1>3 Then $gY1 -= $kDelta
                If $iDelta < 0  And $gY1<$kYmax Then $gY1 += $kDelta
            Else
                If $iDelta > 0 And $gY0>3 Then $gY0 -= $kDelta
                If $iDelta < 0 And $gY0<$kYmax Then $gY0 += $kDelta
                If $iDelta > 0 And $gY1>3 Then $gY1 -= $kDelta
                If $iDelta < 0 And $gY1<$kYmax Then $gY1 += $kDelta
            EndIf
        EndIf
    EndIf
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_MOUSEWHEEL

Func Paint()
    _GDIPlus_GraphicsClear($ghCanvas,0)
    _GDIPlus_GraphicsDrawImageRect($ghCanvas, $ghImage, 0,0, $iW, $iH)
    _GDIPlus_GraphicsDrawLine($ghCanvas, $gX0, $gY0, $gX1, $gY1, $ghPen)
    _GDIPlus_GraphicsDrawImageRect($gGraphicPic, $ghBitmap, 0, 0, $iW, $iH)
EndFunc

Func WM_PAINT()
    _WinAPI_RedrawWindow($gForm1, 0, 0, $RDW_UPDATENOW)
    Paint()
    _WinAPI_RedrawWindow($gForm1, 0, 0, $RDW_VALIDATE)
EndFunc

Func Angle($y, $x) ;return value is in degree
    Local Const $fPi = ACos(-1), $fPi2 = ACos(-1) / 2, $fRad = 180 / $fPi
    Switch $x
        Case ($x > 0)
            Return ATan($y / $x) * $fRad
        Case ($x < 0 And $y >= 0)
            Return ATan($y / $x + $fPi) * $fRad
        Case ($x < 0 And $y < 0)
            Return ATan($y / $x - $fPi) * $fRad
        Case ($x = 0 And $y > 0)
            Return $fPi2 * $fRad
        Case ($x = 0 And $y < 0)
            Return -$fPi2 * $fRad
        Case ($x = 0 And $y = 0)
            Return 0
    EndSwitch
EndFunc

Func GetBestCropArea($hImage) ;not speed optimized!
    Local $iW = _GDIPlus_ImageGetWidth($hImage), $iH = _GDIPlus_ImageGetHeight($hImage), $fRatio = $iW / $iH, $fCenterX = $iW / 2, $fCenterY = $iH / 2
    Local $fPosX, $fPosY, $fW, $fH, $iPixel1, $iPixel2
    For $x = $iW To 0 Step -1
        $fPosX = $fCenterX - $x / 2
        $fPosY = $fCenterY - ($x / 2) / $fRatio
        $fW = $x
        $fH = $x / $fRatio
        $iPixel1 = _GDIPlus_BitmapGetPixel($hImage, $fPosX, $fPosY)
        $iPixel2 = _GDIPlus_BitmapGetPixel($hImage, $fPosX + $fW, $fPosY)
        If $iPixel1 And $iPixel2 Then ExitLoop
    Next
    If Not $x Then Return SetError(1, 0, 0)
    Local $aPos[4] = [$fPosX, $fPosY, $fW, $fH]
    Return $aPos
EndFunc

 

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites
c.haslam

Your approach is surprisingly performant, given that it has a for loop that is executed many times. It appears to me that it will fail if, in traversing the image, it simultaneously encounters two black pixels before reaching a corner triangle. I realize that this is unlikely.

In order to run your code, I ran it on a 1024x768 version of pergola.jpg; otherwise on my 1920x1024 monitor, the Rotate button was off screen.

I have revisited my code, improving the time it takes to do the cropping. While it was recreating the control, it now adds borders to the displayed version, the borders being the colour of the client-area background. What was taking about 130 msec snow  takes about 36. Also, there is now no flicker. The script, with the new code, is below:

#include <ButtonConstants.au3>
#include <GDIPlus.au3>
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>
Opt('MustDeclareVars',1)

GUIRegisterMsg($WM_MOUSEWHEEL, "WM_MOUSEWHEEL")
GUIRegisterMsg($WM_PAINT,'WM_PAINT')

Global $gX0=10,$gX1=580,$gY0,$gY1,$gForm1,$glblPic,$gCtlWid,$gCtlHt,$imgWid,$imgHt
Global $ghCanvas,$ghImage,$ghPen,$gGraphicPic,$ghBitmap

main()

Func main()
$gCtlWid = 893
$gCtlHt = 480
$gForm1 = GUICreate("Form1", $gCtlWid+50, 601, 192, 114)
$glblPic = GUICtrlCreateLabel("", 8, 8, $gCtlWid, $gCtlHt)
Local $btnRotate = GUICtrlCreateButton("Rotate", 472, 560, 65, 25)
Local $btnExit = GUICtrlCreateButton("  Exit  ",600,560)
GUISetState(@SW_SHOW)

    Local $oldY0,$oldY1

    _GDIPlus_Startup()

    $ghPen = _GDIPlus_PenCreate(0xFF999999,2)
    $ghImage = _GDIPlus_ImageLoadFromFile('H:\b\pergola.jpg')
    Local $hnd = GUICtrlGetHandle($glblPic)
    $gGraphicPic = _GDIPlus_GraphicsCreateFromHWND($hnd)
    $gY0 = 400
    $gY1 = 400
    $imgWid = _GDIPlus_ImageGetWidth($ghImage)
    $imgHt = _GDIPlus_ImageGetHeight($ghImage)
    $ghBitmap = _GDIPlus_BitmapCreateFromScan0($imgWid, $imgHt)
    $ghCanvas = _GDIPlus_ImageGetGraphicsContext($ghBitmap)
    ; Loop until the user exits.
    While True
        If $gY0<>$oldY0 Or $gY1<>$oldY1 Then
            Paint()
            $oldY0 = $gY0
            $oldY1 = $gY1
        Else
            Switch GUIGetMsg()
                Case $GUI_EVENT_CLOSE,$btnExit
                    ExitLoop
                Case $btnRotate
                    Local $hImageClone=_GDIPlus_BitmapCreateFromScan0($imgWid, $imgHt)
                    Local $hGfxClone=_GDIPlus_ImageGetGraphicsContext($hImageClone)
                    ; rotate about centre
                    Local $gMatrix = _GDIPlus_MatrixCreate()
                    _GDIPlus_MatrixTranslate($gMatrix, $imgWid/2, $imgHt/2)
                    Local $deg = Angle($gY1-$gY0, $gX1-$gX0)
                    _GDIPlus_MatrixRotate($gMatrix, -$deg)
                    _GDIPlus_MatrixTranslate($gMatrix, -$imgWid/2, -$imgHt/2)
                    _GDIPlus_GraphicsSetTransform($hGfxClone, $gMatrix)

                    _GDIPlus_GraphicsDrawImageRect($hGfxClone,$ghImage,0,0,$imgWid,$imgHt)
                    _GDIPlus_GraphicsDispose($hGfxClone)
                    $hGfxClone=_GDIPlus_ImageGetGraphicsContext($ghImage)
                    ; See deskew notes document
                    Local $ang = Abs($deg*3.14159/180)
                    Local $cos = cos($ang), $sin = sin($ang), $tan = tan($ang)
                    Local $w2cos = $imgWid/2*$cos, $w2sin =  $imgWid/2*$sin
                    Local $h2cos = $imgHt/2*$cos, $h2sin = $imgHt/2*$sin
                    Local $nsrcX = ($imgHt/2 - $w2sin - $h2cos)*$tan - $w2cos + $h2sin + $imgWid/2
                    Local $nsrcY = ($imgWid/2 + $w2cos - $h2sin)*$tan - $w2sin - $h2cos + $imgHt/2

                    Local $nSrcWidth = $imgWid - 2*$nSrcX
                    Local $nSrcHeight = $imgHt - 2*$nSrcY
                    ; crop to screen, centering within control
                    _GDIPlus_GraphicsClear($hGfxClone,0xFFD4D0C8)   ; client area background colour
                    Local $nDstX = ($imgWid-$nSrcWidth)/2
                    Local $nDstY = ($imgHt-$nSrcHeight)/2
                    ; draw a portion of image object $hImageClone on the image that $hGfxClone points to
                    _GDIPlus_GraphicsDrawImageRectRect($hGfxClone,$hImageClone, _
                        $nSrcX,$nSrcY,$nSrcWidth,$nSrcHeight,$nDstX,$nDstY,$nSrcWidth,$nSrcHeight)
                    _GDIPlus_ImageDispose($hImageClone)
                    $gy0 = 400
                    $gy1 = 400
                    Paint()
                    Local $hImageClone2 = _GDIPlus_BitmapCloneArea($ghImage,$nsrcX,$nsrcY,$nSrcWidth,$nSrcHeight)
                    _GDIPlus_ImageSaveToFile($hImageClone2,'H:\b\1.jpg')
                    _GDIPlus_ImageDispose($hImageClone2)
                    ShellExecute('H:\b\1.jpg')
           EndSwitch
        EndIf
    WEnd

    ; Clean up resources
    _GDIPlus_MatrixDispose($gMatrix)
    _GDIPlus_ImageDispose($ghCanvas)
    _GDIPlus_ImageDispose($ghImage)
    _GDIPlus_ImageDispose($ghBitmap)
    _GDIPlus_PenDispose($ghPen)
    _GDIPlus_GraphicsDispose($gGraphicPic)
    _GDIPlus_Shutdown()
EndFunc

Func WM_MOUSEWHEEL($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam, $lParam
    Local Const $MK_SHIFT=0x4,$MK_CONTROL= 0x8
    Local Const $kYmax=$gCtlHt-1,$kDelta=2

    Local $vec = GUIGetCursorInfo($gForm1)
    If $vec[4] = $vec[4]=$glblPic Then
        Local $iDelta = BitShift($wParam, 16)   ; positive = up
        Local $iKeys = _WinAPI_LoWord($wParam)
        If BitAND($iKeys,$MK_CONTROL)=$MK_CONTROL Then
            If BitAND($iKeys,$MK_SHIFT)=$MK_SHIFT Then
                ; do nothing
            Else
                If $iDelta > 0 And $gY0>3 Then $gY0 -= $kDelta
                If $iDelta < 0 And $gY0<$kYmax Then $gY0 += $kDelta
            EndIf
        Else
            If BitAND($iKeys,$MK_SHIFT)=$MK_SHIFT Then
                If $iDelta > 0 And $gY1>3 Then $gY1 -= $kDelta
                If $iDelta < 0  And $gY1<$kYmax Then $gY1 += $kDelta
            Else
                If $iDelta > 0 And $gY0>3 Then $gY0 -= $kDelta
                If $iDelta < 0 And $gY0<$kYmax Then $gY0 += $kDelta
                If $iDelta > 0 And $gY1>3 Then $gY1 -= $kDelta
                If $iDelta < 0 And $gY1<$kYmax Then $gY1 += $kDelta
            EndIf
        EndIf
    EndIf
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_MOUSEWHEEL

Func Paint()
    _GDIPlus_GraphicsClear($ghCanvas,0)
    _GDIPlus_GraphicsDrawImageRect($ghCanvas, $ghImage, 0,0, $gCtlWid, $gCtlHt)
    _GDIPlus_GraphicsDrawLine($ghCanvas, $gX0, $gY0, $gX1, $gY1, $ghPen)
    _GDIPlus_GraphicsDrawImageRect($gGraphicPic, $ghBitmap, 0, 0, $imgWid, $imgHt)
EndFunc

Func WM_PAINT()
    _WinAPI_RedrawWindow($gForm1, 0, 0, $RDW_UPDATENOW)
    Paint()
    _WinAPI_RedrawWindow($gForm1, 0, 0, $RDW_VALIDATE)
EndFunc

Func Angle($y, $x) ;return value is in degree
    Local Const $fPi = ACos(-1), $fPi2 = ACos(-1) / 2, $fRad = 180 / $fPi
    Switch True
        Case ($x > 0)
            Return ATan($y / $x) * $fRad
        Case ($x < 0 And $y >= 0)
            Return ATan($y / $x + $fPi) * $fRad
        Case ($x < 0 And $y < 0)
            Return ATan($y / $x - $fPi) * $fRad
        Case ($x = 0 And $y > 0)
            Return $fPi2 * $fRad
        Case ($x = 0 And $y < 0)
            Return -$fPi2 * $fRad
        Case ($x = 0 And $y = 0)
            Return 0
    EndSwitch
EndFunc

 


Spoiler

CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Similar Content

    • c.haslam
      By c.haslam
      I think that this UDF should return a Property Item that can readily be set as a property of another image. Why would one want to do this? A script might (as one of mine does) edit an image of a .jpg file then write the result to a .jpg file. In editing it, in my case, the number of horizontal and vertical pixels change, and the date/time edited should be set as a property. All other property items remain the same. Other GDI+ users may change other properties.
      So an approach is to copy all the property items from image1 to image2, then update a few properties.
      M$ provides a Property Item class (id, length, pointer to an array of values) but _GDIPlus_ImageGetPropertyItem returns something different.
      I think that, wherever reasonable, UDFs that are wrappers for M$ methods should work like M$'s methods. _GDIPlus_ImageGetPropertyItem does not do this:
      While M$ returns 2 values for a value that is a ratio of 2 numbers,  _GDIPlus_ImageGetPropertyItem returns numerator/denominator as a single value (often a Double). _GDIPlus_ImageSetPropertyItem (when included in GDIPlus.au3) will be unable to set ratio property items properly because it cannot know what the numerator and denominator are. So copying such property items will not work properly. M$ has a Void* buffer where the buffer is an array of values but _GDIPlus_ImageGetPropertyItem() returns the values in the same 1-d array as id, length and type. M$'s approach produces a 1-d array with the same number of elements for all property items while _GDIPlus_ImageGetPropertyItem produces a 1-d array with various numbers of elements. To me, this is not good programming practice. For a photo from a Sony camera, the worst case has 1-d array with 67 elements. When combined into a 2-d array with the property items of all properties, there are 66 rows and 67 columns, with many elements not used. So I suggest that _GDIPlus_ImageGetPropertyItem look like this:
      ; #FUNCTION# ==================================================================================================================== ; Name ..........: cGDIPlus_ImageGetPropertyItem ; Description ...: Gets a specified property item (piece of meta data) from an Image object ; Syntax ........: cGDIPlus_ImageGetPropertyItem($hImage, $iPropID) ; Parameters ....: $hImage - Pointer to an image object ; $iPropID - Identifier of the property item to be retrieved ; Return values .: Success: Array containing the values of the property item ; [0] - identifier ; [1] - size, in bytes, of the value array ; [2] - type of value(s) in the value array ; [3] - value array ; Failure: Sets the @error flag to non-zero, @extended may contain GPSTATUS error code ($GPID_ERR*). ; Author ........: Eukalyptus ; Modified ......: c.haslam ; Remarks .......: types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4, ; unsinged rational = 5, signed byte = 6, undefined = 7, signed long = 9, ; signed rational = 10 ; Related .......: _GDIPlus_ImageGetPropertyIdList ; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx, ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx, ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx ; Example .......: Yes ; =============================================================================================================================== Func cGDIPlus_ImageGetPropertyItem($hImage, $iPropID) Local $iSize = __GDIPlus_ImageGetPropertyItemSize($hImage, $iPropID) If @error Then Return SetError(@error, @extended, -1) Local $tBuffer = DllStructCreate("byte[" & $iSize & "];") Local $pBuffer = DllStructGetPtr($tBuffer) Local $aResult = DllCall($__g_hGDIPDll, "int", "GdipGetPropertyItem", "handle", $hImage, "uint", _ $iPropID, "uint", $iSize, "struct*", $tBuffer) If @error Then Return SetError(@error, @extended, -1) If $aResult[0] Then Return SetError(10, $aResult[0], False) Local $tPropertyItem = DllStructCreate("int id; int length; short type; ptr value;", $pBuffer) Local $iBytes = DllStructGetData($tPropertyItem, "length") Local $pValue = DllStructGetData($tPropertyItem, "value") Local $aRet[4] Local $type = DllStructGetData($tPropertyItem,'type') Local $tValues, $iValues Switch $type Case 2 ;ASCII String $iValues = 1 $tValues = DllStructCreate("char[" & $iBytes & "];", $pValue) Case 3 ;Array of UShort $iValues = Int($iBytes / 2) $tValues = DllStructCreate("ushort[" & $iValues & "];", $pValue) Case 4, 5 ;Array of UInt / Fraction $iValues = Int($iBytes / 4) $tValues = DllStructCreate("uint[" & $iValues & "];", $pValue) Case 9, 10 ;Array of Int / Fraction $iValues = Int($iBytes / 4) $tValues = DllStructCreate("int[" & $iValues & "];", $pValue) Case Else ;Array of Bytes $iValues = 1 $tValues = DllStructCreate("byte[" & $iBytes & "];", $pValue) EndSwitch $aRet[0] = DllStructGetData($tPropertyItem,'id') $aRet[1] = $iBytes $aRet[2] = $type Local $aVals[$iValues] If $type=2 Or $type=7 Then ; ASCII string or undefined $aVals[0] = DllStructGetData($tValues,1) Else For $i = 0 To $iValues-1 $aVals[$i] = DllStructGetData($tValues,1,$i+1) Next EndIf $aRet[3] = $aVals Return $aRet EndFunc And here is an example:
      #include <GDIPlus.au3> #include <Array.au3> Example() Func Example() _GDIPlus_Startup() Local $hImage = _GDIPlus_ImageLoadFromFile(RegRead((@AutoItX64 = True ? "HKLM\SOFTWARE\Wow6432Node\AutoIt v3\AutoIt" : "HKLM\SOFTWARE\AutoIt v3\AutoIt"), "InstallDir") & "\Examples\GUI\Torus.png") If @error Then _GDIPlus_Shutdown() MsgBox(16, "", "An error has occured - unable to load image!", 30) Return False EndIf Local $ar = _GDIPlus_ImageGetPropertyIdList($hImage) Local $vPropNbrs[UBound($ar,1)-1] ; Extract ID numbers For $i = 1 To UBound($ar,1)-1 $vPropNbrs[$i-1] = $ar[$i][0] Next ; Get all property items Local $aPropItems[UBound($vPropNbrs)][4],$vPropItem For $i = 0 To UBound($vPropNbrs)-1 $vPropItem = cGDIPlus_ImageGetPropertyItem($hImage,$vPropNbrs[$i]) For $j = 0 To 3 $aPropItems[$i][$j] = $vPropItem[$j] Next Next ; Collapse values arrays so _ArrayDisplay can display them Local $ar = $aPropItems For $i = 0 To UBound($aPropItems,1)-1 $ar[$i][0] = '0x'&Hex($ar[$i][0],4) $ar[$i][3] = '' For $j = 0 To UBound($aPropItems[$i][3])-1 $ar[$i][3] &= ($aPropItems[$i][3])[$j]&'|' Next $ar[$i][3] = StringTrimRight($ar[$i][3],1) Next _ArrayDisplay($ar) _GDIPlus_Shutdown() EndFunc Unfortunately this example (based on the Help) does not exercise most of the item types, but I do not know of a file with EXIF metadata that is on most PCs. The code for implementing and calling _GDIPlus_ImageSetPropertyItem will be fairly simple.
       
      Your thoughts?
       
    • c.haslam
      By c.haslam
      I have a strange symptom of a problem somewhere in my code, or in a UDF: when I add code to get all properties of a GDI+ image to a 6000-line script,  the variable type of a parameter is reported as a pointer when the caller clearly has this argument as a string. If I comment out my code that gets the properties, the function properly sees the parameter as a string.
      [Edit: The reporting as a pointer and getting image properties are miles apart, both in where they are in the script code and in where they are run.]
      At this point, I am looking for clues as to why this is happening, so the hunt is on!
      I have noticed one odd thing, in _GDIPlus_ImageGetPropertyIdList() I see a line that begins Local $sPropertyTagInfo = . This "line" is split into 2 lines. SCiTE shows that the first line is 2454 characters and the second 2400. Adding these numbers I get 4854 characters.
      But the Help for AutoIt3 Limits/defaults says that MAXLINESIZE, Maximum size for a line of script, is 4096 characters.
      The line would exceed the limit if AutoIt considers the subject line to be 1 line and not 2 lines.
      My question: is this line legal? Is buffer overun possible?
    • c.haslam
      By c.haslam
      I find _GDIPlus_ImageClone in the Help, but neither in SciTe auto-complete nor in GDIPlus.au3
      Shoud this be reported in Trac?
      By searching the forum (and changing the name of the first arguement ot DLLCall, the code appears to be:
      Func _GDIPlus_ImageClone($hImage) Local $aResult = DllCall($__g_hGDIPDll, "uint", "GdipCloneImage", "handle", $hImage, "int*", 0) If @error Then Return SetError(@error, @extended, 0) Return $aResult[2] EndFunc ;==>_GDIPlus_ImageClone  
×

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.