-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuilder.ts
More file actions
107 lines (94 loc) · 2.62 KB
/
builder.ts
File metadata and controls
107 lines (94 loc) · 2.62 KB
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
type DecodedImage = {
paletteIndex: number;
bounds: {
top: number;
right: number;
bottom: number;
left: number;
};
rects: [length: number, colorIndex: number][];
};
type SVGOptions = {
parts: { data: string }[];
palette: string[];
background: string;
viewbox: number[];
size?: number;
};
export const decodeImage = (image: string): DecodedImage => {
const data = image.replace(/^0x/, "");
const paletteIndex = parseInt(data.substring(0, 2), 16);
const bounds = {
top: parseInt(data.substring(2, 4), 16),
right: parseInt(data.substring(4, 6), 16),
bottom: parseInt(data.substring(6, 8), 16),
left: parseInt(data.substring(8, 10), 16),
};
const rects = data.substring(10);
return {
paletteIndex,
bounds,
rects:
rects
?.match(/.{1,4}/g)
?.map((rect) => [
parseInt(rect.substring(0, 2), 16),
parseInt(rect.substring(2, 4), 16),
]) ?? [],
};
};
const getRectLength = (
currentX: number,
drawLength: number,
rightBound: number
): number => {
const remainingPixelsInLine = rightBound - currentX;
return drawLength <= remainingPixelsInLine
? drawLength
: remainingPixelsInLine;
};
export const buildSVG = ({
parts,
palette,
background,
viewbox,
size = 320,
}: SVGOptions): string => {
let openingTag = `<svg viewBox="${viewbox.join(
" "
)}" width="${size}" height="${size}" xmlns="http://www.w3.org/2000/svg" shape-rendering="crispEdges">`;
if (background !== "transparent") {
openingTag += `<rect width="100%" height="100%" fill="#${background}" />`;
}
const svgWithoutEndTag = parts.reduce((result, part) => {
const svgRects: string[] = [];
const { bounds, rects } = decodeImage(part.data);
let currentX = bounds.left;
let currentY = bounds.top;
rects.forEach((draw) => {
let [drawLength, colorIndex] = draw;
const hexColor = palette[colorIndex];
let length = getRectLength(currentX, drawLength, bounds.right);
while (length > 0) {
// Do not push rect if transparent
if (colorIndex !== 0) {
svgRects.push(
`<rect width="${length * 10}" height="10" x="${currentX * 10}" y="${
currentY * 10
}" fill="#${hexColor}" />`
);
}
currentX += length;
if (currentX === bounds.right) {
currentX = bounds.left;
currentY++;
}
drawLength -= length;
length = getRectLength(currentX, drawLength, bounds.right);
}
});
result += svgRects.join("");
return result;
}, openingTag);
return svgWithoutEndTag + "</svg>";
};