-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDither.pas
120 lines (98 loc) · 3.39 KB
/
Dither.pas
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
115
116
117
118
119
120
unit Dither;
{$mode delphiunicode}
interface
uses
Classes, SysUtils, BitmapPixels;
procedure FloydSteinbergDithering(const Image: TBitmapData; const Palette: array of TPixel; const Power: Byte);
implementation
function CalcColorDist(const Color1, Color2: TPixelRec): Integer;
var
DistR, DistG, DistB: Integer;
begin
DistR := Color1.R - Color2.R;
DistG := Color1.G - Color2.G;
DistB := Color1.B - Color2.B;
Result := DistR * DistR + DistG * DistG + DistB * DistB;
end;
function FindClosestColor(const Color: TPixelRec; const Palette: array of TPixel): TPixelRec;
var
MinDist: Integer;
PaletteColor: TPixelRec;
begin
if Length(Palette) = 0 then
Exit($FF000000);
MinDist := High(Integer);
for PaletteColor in Palette do
begin
if MinDist > CalcColorDist(Color, TPixelRec(PaletteColor)) then
begin
Result := TPixelRec(PaletteColor);
MinDist := CalcColorDist(Color, TPixelRec(PaletteColor));
end;
end;
end;
type
TDitherColorError = record
R: Integer;
G: Integer;
B: Integer;
end;
function ClipByte(const Value: Integer): Byte;
begin
if Value > 255 then
Exit(255)
else if Value < 0 then
Exit(0);
Result := Value;
end;
procedure FloydSteinbergDithering(const Image: TBitmapData; const Palette: array of TPixel; const Power: Byte);
var
X, Y, I: Integer;
Color, NewColor: TPixelRec;
Error: TDitherColorError;
CurrLineError: array of TDitherColorError;
NextLineError: array of TDitherColorError;
begin
SetLength(CurrLineError, Image.Width + 2);
SetLength(NextLineError, Image.Width + 2);
for Y := 0 to Image.Height - 1 do
begin
for X := 0 to Image.Width - 1 do
begin
Color := Image.Pixels[X, Y];
Color.A := 255;
Color.R := ClipByte(Color.R + (CurrLineError[X + 1].R * Power div 16) div 255);
Color.G := ClipByte(Color.G + (CurrLineError[X + 1].G * Power div 16) div 255);
Color.B := ClipByte(Color.B + (CurrLineError[X + 1].B * Power div 16) div 255);
NewColor := FindClosestColor(Color, Palette);
Image.Pixels[X, Y] := NewColor;
Error.R := Color.R - NewColor.R;
Error.G := Color.G - NewColor.G;
Error.B := Color.B - NewColor.B;
// [ * 7/16(0) ]
// [ 3/16(1) 5/16(2) 1/16(3) ]
// 0
CurrLineError[X + 2].R := CurrLineError[X + 2].R + 7 * Error.R;
CurrLineError[X + 2].G := CurrLineError[X + 2].G + 7 * Error.G;
CurrLineError[X + 2].B := CurrLineError[X + 2].B + 7 * Error.B;
// 1
NextLineError[X + 0].R := NextLineError[X + 0].R + 3 * Error.R;
NextLineError[X + 0].G := NextLineError[X + 0].G + 3 * Error.G;
NextLineError[X + 0].B := NextLineError[X + 0].B + 3 * Error.B;
// 2
NextLineError[X + 1].R := NextLineError[X + 1].R + 5 * Error.R;
NextLineError[X + 1].G := NextLineError[X + 1].G + 5 * Error.G;
NextLineError[X + 1].B := NextLineError[X + 1].B + 5 * Error.B;
// 3
NextLineError[X + 2].R := NextLineError[X + 2].R + 1 * Error.R;
NextLineError[X + 2].G := NextLineError[X + 2].G + 1 * Error.G;
NextLineError[X + 2].B := NextLineError[X + 2].B + 1 * Error.B;
end;
for I := 0 to High(CurrLineError) do
begin
CurrLineError[I] := NextLineError[I];
NextLineError[I] := Default(TDitherColorError);
end;
end;
end;
end.