Skip to content

Commit 5610e6f

Browse files
committed
(#336) Extended Image class with color mode conversion methods
1 parent 453621c commit 5610e6f

File tree

2 files changed

+101
-27
lines changed

2 files changed

+101
-27
lines changed

lib/image.class.spec.ts

+65-27
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,68 @@
1-
import { Image } from "./image.class";
1+
import {Image} from "./image.class";
2+
import {imageToJimp} from "./provider/io/imageToJimp.function";
3+
import {ColorMode} from "./colormode.enum";
4+
5+
jest.mock("./provider/io/imageToJimp.function", () => {
6+
return {
7+
imageToJimp: jest.fn()
8+
}
9+
});
10+
11+
afterEach(() => {
12+
jest.resetAllMocks();
13+
});
214

315
describe("Image class", () => {
4-
it("should return alphachannel = true for > 3 channels", () => {
5-
const SUT = new Image(200, 200, 123, 4, "id");
6-
expect(SUT.hasAlphaChannel).toBeTruthy();
7-
});
8-
9-
it("should return alphachannel = false for <= 3 channels", () => {
10-
const SUT = new Image(200, 200, 123, 3, "id");
11-
expect(SUT.hasAlphaChannel).toBeFalsy();
12-
});
13-
it("should return alphachannel = false for <= 3 channels", () => {
14-
const SUT = new Image(200, 200, 123, 2, "id");
15-
expect(SUT.hasAlphaChannel).toBeFalsy();
16-
});
17-
it("should return alphachannel = false for <= 3 channels", () => {
18-
const SUT = new Image(200, 200, 123, 1, "id");
19-
expect(SUT.hasAlphaChannel).toBeFalsy();
20-
});
21-
22-
it("should throw for <= 0 channels", () => {
23-
expect(() => new Image(200, 200, 123, 0, "id")).toThrowError("Channel <= 0");
24-
});
25-
26-
it("should have a default pixel density of 1.0", () => {
27-
const SUT = new Image(200, 200, 123, 1, "id");
28-
expect(SUT.pixelDensity).toEqual({ scaleX: 1.0, scaleY: 1.0 });
29-
});
16+
it("should return alphachannel = true for > 3 channels", () => {
17+
const SUT = new Image(200, 200, 123, 4, "id");
18+
expect(SUT.hasAlphaChannel).toBeTruthy();
19+
});
20+
21+
it("should return alphachannel = false for <= 3 channels", () => {
22+
const SUT = new Image(200, 200, 123, 3, "id");
23+
expect(SUT.hasAlphaChannel).toBeFalsy();
24+
});
25+
it("should return alphachannel = false for <= 3 channels", () => {
26+
const SUT = new Image(200, 200, 123, 2, "id");
27+
expect(SUT.hasAlphaChannel).toBeFalsy();
28+
});
29+
it("should return alphachannel = false for <= 3 channels", () => {
30+
const SUT = new Image(200, 200, 123, 1, "id");
31+
expect(SUT.hasAlphaChannel).toBeFalsy();
32+
});
33+
34+
it("should throw for <= 0 channels", () => {
35+
expect(() => new Image(200, 200, 123, 0, "id")).toThrowError("Channel <= 0");
36+
});
37+
38+
it("should have a default pixel density of 1.0", () => {
39+
const SUT = new Image(200, 200, 123, 1, "id");
40+
expect(SUT.pixelDensity).toEqual({scaleX: 1.0, scaleY: 1.0});
41+
});
42+
43+
describe("Colormode", () => {
44+
it("should not try to convert an image to BGR if it already has the correct color mode", async () => {
45+
// GIVEN
46+
const bgrImage = new Image(100, 100, Buffer.from([]), 3, "testImage");
47+
48+
// WHEN
49+
const convertedImage = await bgrImage.toBGR();
50+
51+
// THEN
52+
expect(convertedImage).toBe(bgrImage);
53+
expect(imageToJimp).not.toBeCalledTimes(1)
54+
});
55+
56+
it("should not try to convert an image to RGB if it already has the correct color mode", async () => {
57+
// GIVEN
58+
const rgbImage = new Image(100, 100, Buffer.from([]), 3, "testImage", ColorMode.RGB);
59+
60+
// WHEN
61+
const convertedImage = await rgbImage.toRGB();
62+
63+
// THEN
64+
expect(convertedImage).toBe(rgbImage);
65+
expect(imageToJimp).not.toBeCalledTimes(1)
66+
});
67+
});
3068
});

lib/image.class.ts

+36
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import {imageToJimp} from "./provider/io/imageToJimp.function";
2+
import {ColorMode} from "./colormode.enum";
3+
14
/**
25
* The {@link Image} class represents generic image data
36
*/
@@ -9,6 +12,7 @@ export class Image {
912
* @param data Generic {@link Image} data
1013
* @param channels Amount of {@link Image} channels
1114
* @param id Image identifier
15+
* @param colorMode An images color mode, defaults to {@link ColorMode.BGR}
1216
* @param pixelDensity Object containing scale info to work with e.g. Retina display data where the reported display size and pixel size differ (Default: {scaleX: 1.0, scaleY: 1.0})
1317
*/
1418
constructor(
@@ -17,6 +21,7 @@ export class Image {
1721
public readonly data: any,
1822
public readonly channels: number,
1923
public readonly id: string,
24+
public readonly colorMode: ColorMode = ColorMode.BGR,
2025
public readonly pixelDensity: { scaleX: number; scaleY: number } = {
2126
scaleX: 1.0,
2227
scaleY: 1.0,
@@ -33,4 +38,35 @@ export class Image {
3338
public get hasAlphaChannel() {
3439
return this.channels > 3;
3540
}
41+
42+
/**
43+
* {@link toRGB} converts an {@link Image} from BGR color mode (default within nut.js) to RGB
44+
*/
45+
public async toRGB(): Promise<Image> {
46+
if (this.colorMode === ColorMode.RGB) {
47+
return this;
48+
}
49+
const rgbImage = imageToJimp(this);
50+
return new Image(this.width, this.height, rgbImage.bitmap.data, this.channels, this.id, ColorMode.RGB, this.pixelDensity);
51+
}
52+
53+
/**
54+
* {@link toBGR} converts an {@link Image} from RGB color mode to RGB
55+
*/
56+
public async toBGR(): Promise<Image> {
57+
if (this.colorMode === ColorMode.BGR) {
58+
return this;
59+
}
60+
const rgbImage = imageToJimp(this);
61+
return new Image(this.width, this.height, rgbImage.bitmap.data, this.channels, this.id, ColorMode.BGR, this.pixelDensity);
62+
}
63+
64+
/**
65+
* {@link fromRGBData} creates an {@link Image} from provided RGB data
66+
*/
67+
public static fromRGBData(width: number, height: number, data: Buffer, channels: number, id: string): Image {
68+
const rgbImage = new Image(width, height, data, channels, id);
69+
const jimpImage = imageToJimp(rgbImage);
70+
return new Image(width, height, jimpImage.bitmap.data, channels, id);
71+
}
3672
}

0 commit comments

Comments
 (0)