• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rokucommunity / ninepatcher / #18

03 May 2024 04:07PM UTC coverage: 61.29%. Remained the same
#18

push

web-flow
Merge pull request #1 from rokucommunity/fix-node14

fix node14

52 of 89 branches covered (58.43%)

Branch coverage included in aggregate %.

100 of 159 relevant lines covered (62.89%)

25.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

32.26
/src/Canvas.ts
1
import { Color } from './Color';
1✔
2
import * as Jimp from 'jimp';
1✔
3

4
export class Canvas {
1✔
5
    constructor(
6
        /**
7
         * The color of all background pixels
8
         */
9
        public backgroundColor: Color
2✔
10
    ) { }
11

12
    /**
13
     * A map of rows, containing columns, all indexed by their x or y coordinate.
14
     * Index by Y first, then X
15
     */
16
    private grid = new Map<number, Map<number, Color[]>>();
2✔
17

18
    /**
19
     * Get the lowest-defined x value
20
     */
21
    private get minX() {
22
        const xValues: number[] = [];
×
23
        for (const [, row] of this.grid) {
×
24
            for (const [x] of row) {
×
25
                xValues.push(x);
×
26
            }
27
        }
28
        return Math.min(...xValues);
×
29
    }
30

31
    /**
32
     * Get the highest-defined x value
33
     */
34
    private get maxX() {
35
        const xValues: number[] = [];
×
36
        for (const [, row] of this.grid) {
×
37
            for (const [x] of row) {
×
38
                xValues.push(x);
×
39
            }
40
        }
41
        return Math.max(...xValues);
×
42
    }
43

44
    /**
45
     * Get the lowest-defined y value
46
     */
47
    private get minY() {
48
        return Math.max(...this.grid.keys());
×
49
    }
50

51
    /**
52
     * Get the highest-defined y value
53
     */
54
    public get maxY() {
55
        return Math.max(...this.grid.keys());
×
56
    }
57

58
    public get width() {
59
        return this.maxX - this.minX;
×
60
    }
61

62
    public get height() {
63
        return this.maxY - this.minY;
×
64
    }
65

66
    /**
67
     * Does the grid currently have a value at the given coordinates
68
     */
69
    public isPixelSet(x: number, y: number) {
70
        return !!this.getPixel(x, y);
×
71
    }
72

73
    /**
74
     * Get the value at the specified coordinates. Returns `undefined` if the value was not set
75
     */
76
    public getPixel(x: number, y: number) {
77
        x = Math.round(x);
5✔
78
        y = Math.round(y);
5✔
79
        const colorStack = this.grid.get(y)?.get(x);
5!
80
        if (colorStack) {
5!
81
            return Color.blend(this.backgroundColor, ...colorStack);
5✔
82
        }
83
    }
84

85
    /**
86
     * Get the row at the specified y coordinate. If it doesn't exist, create it
87
     */
88
    private getCell(x: number, y: number) {
89
        y = Math.round(y);
5✔
90
        let row = this.grid.get(y);
5✔
91
        if (!row) {
5!
92
            row = new Map();
5✔
93
            this.grid.set(y, row);
5✔
94
        }
95
        let cell = row.get(x);
5✔
96
        if (!cell) {
5!
97
            cell = [];
5✔
98
            row.set(x, cell);
5✔
99
        }
100

101
        return cell;
5✔
102
    }
103

104
    /**
105
     * Set the color (or color stack) for the given coordinate.
106
     */
107
    public setPixel(color: Color | Color[], x: number, y: number) {
108
        const colorStack = Array.isArray(color) ? color : [color];
5!
109
        x = Math.round(x);
5✔
110
        this.getCell(x, y).push(...colorStack);
5✔
111
    }
112

113
    /**
114
     * Delete a pixel at the specified location. This deletes the whole stack of pixels at that position.
115
     */
116
    public deletePixel(x: number, y: number) {
117
        x = Math.round(x);
×
118
        y = Math.round(y);
×
119
        const row = this.grid.get(y);
×
120
        if (row) {
×
121
            row.delete(x);
×
122
            if (row.size === 0) {
×
123
                this.grid.delete(y);
×
124
            }
125
        }
126
    }
127

128
    /**
129
     * Set the color for each of the given coordinates
130
     */
131
    public setPixels(color: Color | Color[], ...points: Array<[x: number, y: number]>) {
132
        for (let [x, y] of points) {
×
133
            this.setPixel(color, x, y);
×
134
        }
135
    }
136

137
    /**
138
     * Set the color for each of the given coordinates
139
     */
140
    public setIfMissingMany(color: Color, points: Array<[x: number, y: number]>) {
141
        for (let point of points) {
×
142
            this.setPixelIfMissing(color, ...point);
×
143
        }
144
    }
145

146
    /**
147
     * Set the value at the given coordinates only if no image data is there yet.
148
     */
149
    public setPixelIfMissing(color: Color, x: number, y: number) {
150
        if (!this.isPixelSet(x, y)) {
×
151
            this.setPixel(color, x, y);
×
152
        }
153
    }
154

155
    /**
156
     * Translate all the pixels by this amount
157
     */
158
    public translate(xOffset: number, yOffset: number) {
159
        const canvas = new Canvas(this.backgroundColor);
×
160

161
        for (const [y, row] of this.grid) {
×
162
            for (const [x, color] of row) {
×
163
                canvas.setPixel(color, x + xOffset, y + yOffset);
×
164
            }
165
        }
166
        this.grid = canvas.grid;
×
167
    }
168

169
    public write(outPath: string) {
170
        let image = new Jimp(this.maxX, this.maxY, this.backgroundColor.toInteger(), (err) => {
×
171
            if (err) {
×
172
                throw err;
×
173
            }
174
        });
175
        for (const [y, row] of this.grid) {
×
176
            for (const [x, colorStack] of row) {
×
177
                const finalColor = Color.blend(this.backgroundColor, ...colorStack);
×
178
                image.setPixelColor(finalColor?.toInteger(), x, y);
×
179
            }
180
        }
181
        return image.write(outPath);
×
182
    }
183

184
    public clone() {
185
        const clone = new Canvas(this.backgroundColor);
×
186
        for (const [y, row] of this.grid) {
×
187
            for (const [x, color] of row) {
×
188
                clone.setPixel(color, x, y);
×
189
            }
190
        }
191
        return clone;
×
192
    }
193
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc