-
Notifications
You must be signed in to change notification settings - Fork 72
/
Copy pathclass_ImageEqual.ahk
114 lines (90 loc) · 3.89 KB
/
class_ImageEqual.ahk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#include ImagePut.ahk
ImageEqual(images*) {
return ImageEqual.call(images*)
}
class ImageEqual extends ImagePut {
call(images*) {
if (images.Count() == 0)
return false
if (images.Count() == 1)
return true
this.gdiplusStartup()
; Convert the images to pBitmaps (byte arrays).
for i, image in images {
try type := this.DontVerifyImageType(image)
catch
try type := this.ImageType(image)
catch { ; Not a valid image.
result := false
break
}
if (A_Index == 1) {
pBitmap1 := this.toBitmap(type, image)
} else {
pBitmap2 := this.toBitmap(type, image)
result := this.isBitmapEqual(pBitmap1, pBitmap2)
DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap2)
if (result)
continue
else {
result := false
break
}
}
}
DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap1)
this.gdiplusShutdown()
return result
}
isBitmapEqual(ByRef pBitmap1, ByRef pBitmap2, Format := 0x26200A) {
; Make sure both bitmaps are valid pointers.
if !(pBitmap1 && pBitmap2)
return false
; Check if pointers are identical.
if (pBitmap1 == pBitmap2)
return true
; The two bitmaps must be the same size.
DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap1, "uint*", Width1)
DllCall("gdiplus\GdipGetImageWidth", "ptr", pBitmap2, "uint*", Width2)
DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap1, "uint*", Height1)
DllCall("gdiplus\GdipGetImageHeight", "ptr", pBitmap2, "uint*", Height2)
; Match bitmap dimensions.
if (Width1 != Width2 || Height1 != Height2)
return false
; Create a RECT with the width and height and two empty BitmapData.
VarSetCapacity(Rect, 16, 0) ; sizeof(Rect) = 16
, NumPut( Width1, Rect, 8, "uint") ; Width
, NumPut( Height1, Rect, 12, "uint") ; Height
; Do this twice.
while ((i++:=i?i:0) < 2) { ; for(int i = 0; i < 2; i++)
; Create a BitmapData structure.
VarSetCapacity(BitmapData%i%, 16+2*A_PtrSize, 0) ; sizeof(BitmapData) = 24, 32
; Transfer the pixels to a read-only buffer. Avoid using a different PixelFormat.
DllCall("gdiplus\GdipBitmapLockBits"
, "ptr", pBitmap%i%
, "ptr", &Rect
, "uint", 1 ; ImageLockMode.ReadOnly
, "int", Format ; Format32bppArgb is fast.
, "ptr", &BitmapData%i%)
; Get Stride (number of bytes per horizontal line).
Stride%i% := NumGet(BitmapData%i%, 8, "int")
; If the Stride is negative, clone the image to make it top-down; redo the loop.
if (Stride%i% < 0) {
DllCall("gdiplus\GdipCloneImage", "ptr", pBitmap%i%, "ptr*", pBitmapClone)
DllCall("gdiplus\GdipDisposeImage", "ptr", pBitmap%i%) ; Permanently deletes.
pBitmap%i% := pBitmapClone
i-- ; "Let's go around again! Ha!" https://bit.ly/2AWWcM3
}
; Get Scan0 (top-left first pixel).
Scan0%i% := NumGet(BitmapData%i%, 16, "ptr")
}
; RtlCompareMemory preforms an unsafe comparison stopping at the first different byte.
size := Stride1 * Height1
byte := DllCall("ntdll\RtlCompareMemory", "ptr", Scan01+0, "ptr", Scan02+0, "uptr", size, "uptr")
; Unlock Bitmaps. Since they were marked as read only there is no copy back.
DllCall("gdiplus\GdipBitmapUnlockBits", "ptr", pBitmap1, "ptr", &BitmapData1)
DllCall("gdiplus\GdipBitmapUnlockBits", "ptr", pBitmap2, "ptr", &BitmapData2)
; Compare stopped byte.
return (byte == size) ? true : false
}
} ; End of ImageEqual class.