My main source of confusion is whether or not to 1) use a forward array to hold the next rows error values (only modifying the current pixel), OR 2) directly modify the pixels in the next row as I go (5 pixels modified per pixel analyzed for Floyd-Steinberg)?
The reason I'm confused is because general text readings seem to suggest the need for an array (simply the size of 1 row and usually plus one or two extra). BUT so many source codes ive seen seem to directly modify the forward pixels, and i'm not sure but that seems incorrect because if you go 0<>255 you lose the propagated error information? hence the need to store in forward array? So im wondering if perhaps those direct-mod implementations are actually incorrect, even though they still result in what looks like a valid dither
Anyway here's my attempt at both types of implementation, and sure enough they produce different results (although im not 100% sure both implementations are correct!). Im not concerned about speed, just readability as i just want to get my head around it, so Plot() is my friend lol

I would really appreciate it if anyone with experience in this field could help clarify this, and check my implementations

The forward-array version is based on the source code example at https://omohundro.files.wordpress.com/2 ... hering.pdf which is one of the few examples i could find that actually uses a forward array.
[edit]Disregard the Array version of this Floyd-Steinberg test, the implementation is wrong. Scroll down for correct Array versions using Sierra Lite and JJN[/edit]
Code: Select all
#PBBlack = $000000
#PBWhite = $FFFFFF
Procedure.i TruncF0255 (a.f)
If a > 255: a = 255: ElseIf a < 0: a = 0: EndIf
ProcedureReturn a
EndProcedure
Procedure RGBtoLum(c.l) ;Get luminosity (the min and max RGB levels / 2)
r = (c & $FF) : g = ((c & $FF00) >> 8) : b = ((c & $FF0000) >> 16)
If g < r: min = g: Else: min = r: EndIf
If b < min: min = b: EndIf
If g > r: max = g: Else: max = r: EndIf
If b > max: max = b: EndIf
lum = (max + min) >> 1 ;/ 2.0
ProcedureReturn lum
EndProcedure
;Floyd-Steinberg:
; * 7 = * 0.4375
; 3 5 1 (1/16) 0.1875 0.3125 0.0625
Procedure DoFloydARRAY(hImg)
Protected nextpixel.f, ierror.f, oldpixel.f, lr.f
StartDrawing(ImageOutput(hImg))
width = ImageWidth(hImg): height = ImageHeight(hImg)
Dim xline.f(width+1)
For y = 2 To height-3 ;completely avoid edges for this test just to keep things easy
lr = 0
For x = 2 To width-3
oldpixel = Truncf0255(RGBtoLum(Point(x,y)) + xline(x))
If oldpixel < 128
newpixel = 0
Plot(x,y, #PBBlack)
Else
newpixel = 255
Plot(x,y, #PBWhite)
EndIf
quant_error.f = oldpixel - newpixel
xline(x-1) + (0.1875 * quant_error)
xline(x) = (0.3125 * quant_error) + lr
xline(x+1) + (0.0625 * quant_error)
lr = (0.4375 * quant_error)
Next x
Next y
StopDrawing()
EndProcedure
Procedure DoFloydDIRECT(hImg)
Protected nextpixel.f, ierror.f
StartDrawing(ImageOutput(hImg))
width = ImageWidth(hImg): height = ImageHeight(hImg)
For y = 2 To height-3 ;completely avoid edges for this test just to keep things easy
For x = 2 To width-3
oldpixel = RGBtoLum(Point(x,y))
If oldpixel < 128
newpixel = 0
Plot(x,y, #PBBlack)
Else
newpixel = 255
Plot(x,y, #PBWhite)
EndIf
quant_error.f = oldpixel - newpixel
newx.f = TruncF0255(RGBtoLum(Point(x+1,y)) + (0.4375 * quant_error))
Plot(x+1,y, RGB(newx,newx,newx))
newx.f = TruncF0255(RGBtoLum(Point(x-1,y+1)) + (0.1875 * quant_error))
Plot(x-1,y+1, RGB(newx,newx,newx))
newx.f = TruncF0255(RGBtoLum(Point(x,y+1)) + (0.3125 * quant_error))
Plot(x,y+1, RGB(newx,newx,newx))
newx.f = TruncF0255(RGBtoLum(Point(x+1,y+1)) + (0.0625 * quant_error))
Plot(x+1,y+1, RGB(newx,newx,newx))
Next x
Next y
StopDrawing()
EndProcedure
UsePNGImageDecoder()
UseJPEGImageDecoder()
If LoadImage(0, "C:\temp\any.jpg") = 0
MessageRequester("Fail","Loadimage failed"): End
EndIf
If OpenWindow(0, 0, 0, ImageWidth(0)*2, ImageHeight(0)+10, "Floyd-Steinberg Test", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
hCopy = CopyImage(0,#PB_Any)
DoFloydARRAY(hCopy) ;v1 - use forward array, only modify current pixel
;DoFloydDIRECT(hCopy) ;v2 - directly modify forward pixels
ImageGadget(0, 0, 0, 200, 200, ImageID(0))
ImageGadget(1, ImageWidth(0), 0, 200, 200, ImageID(hCopy))
Repeat
Event = WaitWindowEvent()
Until Event = #PB_Event_CloseWindow
EndIf