Code: Select all
DrawWuLine(pDC, X0, Y0, X1, Y1, BaseColor, NumLevels:=256, IntensityBits:=8){
/*
https://www.codeproject.com/Articles/13360/Antialiasing-Wu-Algorithm
Arguments:
+ pDC is where line is drawn. Pass hSourceDC from XGraph.
+ (X0,Y0) is start point of line.
+ (X1, Y1) is end point of line.
+ BaseColor is intensity of line. Pass hex 0xBBGGRR in ahk.
+ NumLevels is number of gray scale levels. Pass 256.
+ IntensityBits denotes bits used to represent color component. Pass 8.
*/
/*
Use DrawWuLine() instead of LineTo() in XGraph_Plot()
X0 and Y0 can be the current position:
VarSetCapacity(CurrPos, 8, 0)
DllCall( "GetCurrentPositionEx" , "Ptr" , hSourceDC , "Ptr" , &CurrPos )
CurrPosX := NumGet(CurrPos,0,"Int")
CurrPosY := NumGet(CurrPos,4,"Int")
DrawWuLine(hSourceDC, CurrPosX, CurrPosY, SetVal, MY2, 0x808080)
Then move the current position to the newly plotted position for the next iteration
DllCall( "MoveToEx"
, "Ptr", hSourceDC
, "Int", SetVal
, "Int", MY2
, "Ptr", 0 )
To save CPU you can do just a single final BitBlt() to the hTargetDC after you've drawn all your Wu lines.
*/
/*
unsigned short IntensityShift, ErrorAdj, ErrorAcc;
unsigned short ErrorAccTemp, Weighting, WeightingComplementMask;
short DeltaX, DeltaY, Temp, XDir
*/
VarSetCapacity(IntensityShift,2,0)
VarSetCapacity(ErrorAdj,2,0)
VarSetCapacity(ErrorAcc,2,0)
VarSetCapacity(ErrorAccTemp,2,0)
VarSetCapacity(Weighting,2,0)
VarSetCapacity(WeightingComplementMask,2,0)
VarSetCapacity(DeltaX,2,0)
VarSetCapacity(DeltaY,2,0)
VarSetCapacity(Temp,2,0)
VarSetCapacity(XDir,2,0)
;Make sure the line runs top to bottom
if (Y0 > Y1){
Temp := Y0 , Y0 := Y1 , Y1 := Temp
Temp := X0 , X0 := X1 , X1 := Temp
}
/*
Draw the initial pixel, which is always exactly intersected by
the line and so needs no weighting
*/
DllCall("SetPixel", "Ptr", pDC, "Int", X0, "Int", Y0, "UInt", BaseColor)
DeltaX := X1 - X0
if (DeltaX >= 0)
XDir := 1
else
{
XDir := -1
DeltaX := -DeltaX ;make DeltaX positive
}
/*
Special-case horizontal, vertical, and diagonal lines, which
require no weighting because they go right through the center of
every pixel
*/
DeltaY := Y1 - Y0
if (DeltaY == 0){
;Horizontal line
loop
{
If (DeltaX = 0)
break
Else
{
X0 += XDir
DllCall("SetPixel", "Ptr", pDC, "Int", X0, "Int", Y0, "UInt", BaseColor)
DeltaX--
}
}
return
}
if (DeltaX == 0) {
;Vertical line
loop
{
Y0++
DllCall("SetPixel", "Ptr", pDC, "Int", X0, "Int", Y0, "UInt", BaseColor)
DeltaY--
If (DeltaY = 0)
break
}
return
}
if (DeltaX == DeltaY) {
;Diagonal line
loop
{
X0 += XDir
Y0++
DllCall("SetPixel", "Ptr", pDC, "Int", X0, "Int", Y0, "UInt", BaseColor)
DeltaY--
If (DeltaY = 0)
break
}
return
}
;Line is not horizontal, diagonal, or vertical
ErrorAcc := 0 ;initialize the line error accumulator to 0
;# of bits by which to shift ErrorAcc to get intensity level
IntensityShift := 16 - IntensityBits
/*
Mask used to flip all bits in an intensity weighting, producing the
result (1 - intensity weighting)
*/
WeightingComplementMask := NumLevels - 1
;Is this an X-major or Y-major line?
if (DeltaY > DeltaX) {
/*
Y-major line; calculate 16-bit fixed-point fractional part of a
pixel that X advances each time Y advances 1 pixel, truncating the
result so that we won't overrun the endpoint along the X axis
*/
ErrorAdj := (DeltaX << 16) / DeltaY
;Draw all pixels other than the first and last
loop
{
DeltaY--
If (DeltaY){
ErrorAccTemp := ErrorAcc ;remember current accumulated error
ErrorAcc += ErrorAdj ;calculate error for next pixel
if (ErrorAcc <= ErrorAccTemp)
X0 += XDir ;The error accumulator turned over, so advance the X coord
Y0++ ;Y-major, so always advance Y
/*
The IntensityBits most significant bits of ErrorAcc give us the
intensity weighting for this pixel, and the complement of the
weighting for the paired pixel
*/
Weighting := ErrorAcc >> IntensityShift
;--- In ahk, problem seems to be occuring here? ----
;Original is just these two lines
;DllCall("SetPixel", "Ptr", pDC, "Int", X0, "Int", Y0, "UInt", BaseColor+Weighting)
;DllCall("SetPixel", "Ptr", pDC, "Int", X0+XDir, "Int", Y0, "UInt", BaseColor+(Weighting ^ WeightingComplementMask))
;But Weighting seems to be an int between 0 and 255, therefore convert Basecolor (hex) to RGB ints before adding it?
Red1 := (BaseColor & 0xFF) + (Weighting) ;or BaseColor * (Weighting/255)?
Green1 := (BaseColor >> 8 & 0xFF) + (Weighting)
Blue1 := (BaseColor >> 16 & 0xFF) + (Weighting)
NewColor1 := format("0x{1:02x}{2:02x}{3:02x}", Red1, Green1, Blue1)
DllCall("SetPixel", "Ptr", pDC, "Int", X0, "Int", Y0, "UInt", NewColor1)
Red2 := (BaseColor & 0xFF) + (Weighting^WeightingComplementMask) ; ^ = bitwise-XOR
Green2 := (BaseColor >> 8 & 0xFF) + (Weighting^WeightingComplementMask)
Blue2 := (BaseColor >> 16 & 0xFF) + (Weighting^WeightingComplementMask)
NewColor2 := format("0x{1:02x}{2:02x}{3:02x}", Red2, Green2, Blue2)
DllCall("SetPixel", "Ptr", pDC, "Int", X0+XDir, "Int", Y0, "UInt",NewColor2)
;MsgBox % "Debug Y-major`nWeighting: " . Weighting . "`nNewColor1: " . NewColor1 . "`nNewColor2: " . NewColor2
;-----------------------------------------------------
}
Else
break
}
/*
Draw the final pixel, which is always exactly intersected by the line
and so needs no weighting
*/
DllCall("SetPixel", "Ptr", pDC, "Int", X1, "Int", Y1, "UInt", BaseColor)
return
}
/*
It's an X-major line; calculate 16-bit fixed-point fractional part of a
pixel that Y advances each time X advances 1 pixel, truncating the
result to avoid overrunning the endpoint along the X axis
*/
ErrorAdj := (DeltaY << 16) / DeltaX
;Draw all pixels other than the first and last
loop
{
DeltaX--
If (DeltaX){
ErrorAccTemp := ErrorAcc ;remember current accumulated error
ErrorAcc += ErrorAdj ;calculate error for next pixel
if (ErrorAcc <= ErrorAccTemp){
;The error accumulator turned over, so advance the Y coord
Y0++
}
X0 += XDir ;X-major, so always advance X
/*
The IntensityBits most significant bits of ErrorAcc give us the
intensity weighting for this pixel, and the complement of the
weighting for the paired pixel
*/
Weighting := ErrorAcc >> IntensityShift
;--- In ahk, problem seems to be occuring here? ----
;Original is just these two lines
;DllCall("SetPixel", "Ptr", pDC, "Int", X0, "Int", Y0, "UInt", BaseColor + Weighting)
;DllCall("SetPixel", "Ptr", pDC, "Int", X0, "Int", Y0+1, "UInt", BaseColor+(Weighting ^ WeightingComplementMask))
;But Weighting seems to be an int between 0 and 255, therefore convert Basecolor (hex) to RGB ints before adding it?
Red1 := (BaseColor & 0xFF) + (Weighting) ;or BaseColor * (Weighting/255)?
Green1 := (BaseColor >> 8 & 0xFF) + (Weighting)
Blue1 := (BaseColor >> 16 & 0xFF) + (Weighting)
NewColor1 := format("0x{1:02x}{2:02x}{3:02x}", Red1, Green1, Blue1)
DllCall("SetPixel", "Ptr", pDC, "Int", X0, "Int", Y0, "UInt", NewColor1)
Red2 := (BaseColor & 0xFF) + (Weighting^WeightingComplementMask) ^ = bitwise-XOR
Green2 := (BaseColor >> 8 & 0xFF) + (Weighting^WeightingComplementMask)
Blue2 := (BaseColor >> 16 & 0xFF) + (Weighting^WeightingComplementMask)
NewColor2 := format("0x{1:02x}{2:02x}{3:02x}", Red2, Green2, Blue2)
DllCall("SetPixel", "Ptr", pDC, "Int", X0, "Int", Y0+1, "UInt", NewColor2)
;MsgBox % "Debug X-major`nWeighting: " . Weighting . "`nNewColor1: " . NewColor1 . "`nNewColor2: " . NewColor2
;-----------------------------------------------------
}
Else
break
}
/*
Draw the final pixel, which is always exactly intersected by the line
and so needs no weighting
*/
DllCall("SetPixel", "Ptr", pDC, "Int", X1, "Int", Y1, "UInt", BaseColor)
}
In the meantime I managed to get a satisfactory result by applying the A_ScreenDPI/96 scaling at an earlier point in my draw function instead of to the final 96dpi plotted points. This way the line is based on the highest resolution source data rather than being simply "upscaled" from the 96dpi final plotted points, which was causing all the really nasty aliasing. Also as mentioned increasing the pen size to 2 pixels, and reducing the contrast between the plotted line and the background colour helps a lot too.