-
Notifications
You must be signed in to change notification settings - Fork 22
/
bitmap_dither.lua
121 lines (102 loc) · 3.26 KB
/
bitmap_dither.lua
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
--bitmap dithering.
--Written by Cosmin Apreutesei. Public domain.
local bitmap = require'bitmap'
bitmap.dither = {}
--floyd-steinberg dithering
local function fs_dither_4c(x, maxval, r1, g1, b1, a1, r0, g0, b0, a0)
return
math.min(r0 + bit.rshift(x * r1, 4), maxval),
math.min(g0 + bit.rshift(x * g1, 4), maxval),
math.min(b0 + bit.rshift(x * b1, 4), maxval),
math.min(a0 + bit.rshift(x * a1, 4), maxval)
end
function bitmap.dither.fs(src, rbits, gbits, bbits, abits)
local colortype = bitmap.colortype(src)
assert(#colortype.channels == 4, 'invalid colortype')
local maxbits = colortype.bpc
local maxval = colortype.max
local getpixel, setpixel = bitmap.pixel_interface(src)
local rmask = 2^(maxbits-rbits)-1
local gmask = 2^(maxbits-gbits)-1
local bmask = 2^(maxbits-bbits)-1
local amask = 2^(maxbits-abits)-1
for y = 0, src.h-1 do
for x = 0, src.w-1 do
local r0, g0, b0, a0 = getpixel(x, y)
local r1 = bit.band(r0, rmask)
local g1 = bit.band(g0, gmask)
local b1 = bit.band(b0, bmask)
local a1 = bit.band(a0, amask)
setpixel(x, y,
bit.band(r0, maxval-rmask),
bit.band(g0, maxval-gmask),
bit.band(b0, maxval-bmask),
bit.band(a0, maxval-amask))
if x < src.w-1 then
setpixel(x+1, y, fs_dither_4c(7, maxval, r1, g1, b1, a1, getpixel(x+1, y)))
end
if y < src.h-1 and x > 0 then
setpixel(x-1, y+1, fs_dither_4c(3, maxval, r1, g1, b1, a1, getpixel(x-1, y+1)))
end
if y < src.h-1 then
setpixel(x, y+1, fs_dither_4c(5, maxval, r1, g1, b1, a1, getpixel(x, y+1)))
end
if y < src.h-1 and x < src.w-1 then
setpixel(x+1, y+1, fs_dither_4c(1, maxval, r1, g1, b1, a1, getpixel(x+1, y+1)))
end
end
end
end
--ordered dithering
local tmap = {} --threshold maps from wikipedia
tmap[2] = {[0] =
{[0] = 1, 3},
{[0] = 4, 2}}
tmap[3] = {[0] =
{[0] = 3, 7, 4},
{[0] = 6, 1, 9},
{[0] = 2, 8, 5}}
tmap[4] = {[0] =
{[0] = 1, 9, 3, 11},
{[0] = 13, 5, 15, 7},
{[0] = 4, 12, 2, 10},
{[0] = 16, 8, 14, 6}}
tmap[8] = {[0] =
{[0] = 1, 49, 13, 61, 4, 52, 16, 64},
{[0] = 33, 17, 45, 29, 36, 20, 48, 32},
{[0] = 9, 57, 5, 53, 12, 60, 8, 56},
{[0] = 41, 25, 37, 21, 44, 28, 40, 24},
{[0] = 3, 51, 15, 63, 2, 50, 14, 62},
{[0] = 35, 19, 47, 31, 34, 18, 46, 30},
{[0] = 11, 59, 7, 55, 10, 58, 6, 54},
{[0] = 43, 27, 39, 23, 42, 26, 38, 22}}
--NOTE: actual clipping of the low bits is not done here, it will be done
--naturally when converting the bitmap to lower bpc.
local ordered_dither = {}
ordered_dither[4] = function(t, maxval, r, g, b, a)
return
math.min(r + t, maxval),
math.min(g + t, maxval),
math.min(b + t, maxval),
math.min(a + t, maxval)
end
ordered_dither[2] = function(t, maxval, g, a)
return
math.min(g + t, maxval),
math.min(a + t, maxval)
end
function bitmap.dither.ordered(src, mapsize)
local colortype = bitmap.colortype(src)
local maxval = colortype.max
local kernel = assert(ordered_dither[#colortype.channels], 'invalid colortype')
local getpixel, setpixel = bitmap.pixel_interface(src)
local tmap = assert(tmap[mapsize], 'invalid map size')
for y = 0, src.h-1 do
local tmap = tmap[bit.band(y, mapsize-1)]
for x = 0, src.w-1 do
local t = tmap[bit.band(x, mapsize-1)]
setpixel(x, y, kernel(t, maxval, getpixel(x, y)))
end
end
end
if not ... then require'bitmap_demo' end