-
Notifications
You must be signed in to change notification settings - Fork 2
/
PNTImage.cs
149 lines (120 loc) · 4.26 KB
/
PNTImage.cs
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
namespace DyeAtlas
{
public class PNTImage
{
public readonly Palette palette;
public uint version;
public int width;
public int height;
public uint revision;
public int size;
public byte[] bits;
public PNTImage(Palette _palette)
{
palette = _palette;
}
public static PNTImage GenerateFromBitmap(Palette pal, Bitmap bmp, bool dither, bool hsvCompare)
{
PNTImage pnt = new PNTImage(pal);
pnt.version = 1;
pnt.width = bmp.Width;
pnt.height = bmp.Height;
pnt.revision = 1;
pnt.size = pnt.width * pnt.height;
pnt.bits = new byte[pnt.width * pnt.height];
Bitmap scratch = (Bitmap)bmp.Clone();
for (int y = 0; y < pnt.height; y++)
for (int x = 0; x < pnt.width; x++)
{
WorkColor clr = scratch.GetPixel(x, y);
pnt.SetPixel(x, y, clr, hsvCompare);
// if transparent, then no further processing
if (clr.Transparent)
continue;
// dithering
if (dither)
{
// Floyd–Steinberg
WorkColor newClr = pnt.GetPixel(x, y).Value;
WorkColor error = clr - newClr;
DitherHelper(scratch, x + 1, y, error * 7.0 / 16.0);
DitherHelper(scratch, x - 1, y + 1, error * 3.0 / 16.0);
DitherHelper(scratch, x, y + 1, error * 5.0 / 16.0);
DitherHelper(scratch, x + 1, y + 1, error * 1.0 / 16.0);
}
}
return pnt;
}
private static void DitherHelper(Bitmap bmp, int x, int y, WorkColor error)
{
// slow; should be precached
int w = bmp.Width;
int h = bmp.Height;
if (x < 0 || x >= w || y < 0 || y >= h)
return;
WorkColor clr = bmp.GetPixel(x, y);
// dont affect if transparent
if (clr.Transparent)
return;
bmp.SetPixel(x, y, clr + error);
}
public static PNTImage LoadPNT(Palette pal, string file)
{
using (Stream fs = File.OpenRead(file))
using (BinaryReader br = new BinaryReader(fs))
{
PNTImage pnt = new PNTImage(pal);
pnt.version = br.ReadUInt32();
pnt.width = br.ReadInt32();
pnt.height = br.ReadInt32();
pnt.revision = br.ReadUInt32();
pnt.size = br.ReadInt32();
pnt.bits = br.ReadBytes(pnt.size);
return pnt;
}
}
public void Save(string file)
{
using(Stream fs = File.Create(file))
using (BinaryWriter bw = new BinaryWriter(fs))
{
bw.Write(version);
bw.Write(width);
bw.Write(height);
bw.Write(revision);
bw.Write(size);
bw.Write(bits);
}
}
public Color? GetPixel(int x, int y)
{
if (x < 0 || x >= width || y < 0 || y >= height)
return null;
return palette.Get(bits[x + y * width]);
}
public void SetPixel(int x, int y, Color clr, bool hsvCompare)
{
if (x < 0 || x >= width || y < 0 || y >= height)
return;
bits[x + y * width] = (byte)palette.FindClosestIndex(clr, hsvCompare);
}
public Bitmap GenerateBitmap()
{
Bitmap bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{
bmp.SetPixel(x, y, palette.Get(bits[x + y * width]) ?? Color.FromArgb(0, 0, 0, 0));
}
return bmp;
}
}
}