Skip to content

Commit

Permalink
feat: allow rotate & crop for bitDepth = 1 images (#596)
Browse files Browse the repository at this point in the history
  • Loading branch information
Fa-b committed Aug 1, 2022
1 parent 551c334 commit 622b909
Show file tree
Hide file tree
Showing 6 changed files with 470 additions and 81 deletions.
42 changes: 42 additions & 0 deletions src/image/transform/__tests__/crop.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,45 @@ describe('check the crop transform', function () {
}).toThrow(/size is out of range/);
});
});

describe('check the crop transform on binary image', function () {
let image;
beforeEach(function () {
image = new Image(5, 5, [0x03, 0xce, 0xf7, 0x80], { kind: 'BINARY' });
});

it('check the right extract for GREY image', function () {
let result = image.crop();
expect(getHash(result)).toBe(getHash(image));

result = image.crop({
x: 2,
y: 2,
});
expect(Array.from(result.data)).toStrictEqual([0xff, 0x80]);

result = image.crop({
x: 0,
y: 0,
height: 2,
width: 2,
});
expect(Array.from(result.data)).toStrictEqual([0x10]);

result = image.crop({
x: 2,
y: 2,
height: 2,
width: 2,
});
expect(Array.from(result.data)).toStrictEqual([0xf0]);

result = image.crop({
x: 1,
y: 3,
height: 1,
width: 4,
});
expect(Array.from(result.data)).toStrictEqual([0xf0]);
});
});
56 changes: 55 additions & 1 deletion src/image/transform/__tests__/rotate.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Image } from 'test/common';
import { Image, getHash } from 'test/common';

describe('check the rotate transform', function () {
it('90 degrees clockwise grey', function () {
Expand Down Expand Up @@ -26,6 +26,53 @@ describe('check the rotate transform', function () {
expect(Array.from(result.data)).toStrictEqual([6, 5, 4, 3, 2, 1]);
});

it('No actual rotation', function () {
const image = new Image(3, 2, [1, 2, 3, 4, 5, 6], { kind: 'GREY' });

const result = image.rotate(360);
expect(getHash(result)).toStrictEqual(getHash(image));
});

it('45 degrees binary', function () {
const image = new Image(
8,
8,
[0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00],
{ kind: 'BINARY' },
);

const result = image.rotate(45);
expect(Array.from(result.data)).toStrictEqual([
241, 252, 31, 17, 199, 17, 241, 62, 115, 159, 39, 241, 255, 127, 255, 128,
]);
});

it('90 degrees binary', function () {
const image = new Image(8, 4, [0x00, 0x00, 0xaa, 0x55], { kind: 'BINARY' });

const result = image.rotate(90);
expect(Array.from(result.data)).toStrictEqual([0x48, 0x48, 0x48, 0x48]);
});

it('180 degrees binary', function () {
const image = new Image(8, 4, [0x00, 0x00, 0xaa, 0x55], { kind: 'BINARY' });

const result = image.rotate(180);
expect(Array.from(result.data)).toStrictEqual([0xaa, 0x55, 0x00, 0x00]);
});

it('270 degrees binary', function () {
const image = new Image(
8,
8,
[0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00],
{ kind: 'BINARY' },
);

const result = image.rotate(270);
expect(getHash(result)).toStrictEqual(getHash(image));
});

it('negative angle grey', function () {
const image = new Image(3, 2, [1, 2, 3, 4, 5, 6], { kind: 'GREY' });
const rotate90 = image.rotate(90);
Expand All @@ -34,4 +81,11 @@ describe('check the rotate transform', function () {
Array.from(rotateMin270.data),
);
});

it('invalid argument types', function () {
expect(function () {
const image = new Image(3, 2, [1, 2, 3, 4, 5, 6], { kind: 'GREY' });
image.rotate('45°');
}).toThrow(/angle must be a number/);
});
});
62 changes: 62 additions & 0 deletions src/image/transform/__tests__/rotateFree.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Image } from 'test/common';

import rotateFree from '../rotateFree';

describe('check the rotate transform with free rotation', function () {
it('GREY image', function () {
let image = new Image(
Expand All @@ -20,4 +22,64 @@ describe('check the rotate transform with free rotation', function () {
4, 3, 255, 255, 255, 255, 255, 3, 255, 255, 255,
]);
});

it('GREY image bilinear', function () {
let image = new Image(
5,
5,
[
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 2, 2, 2, 0, 1, 2, 4, 3, 0, 1, 2, 3,
3,
],
{ kind: 'GREY' },
);

let result = image.rotate(45, { interpolation: 'bilinear' });

expect(Array.from(result.data)).toStrictEqual([
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255,
255, 0, 1, 0, 255, 255, 255, 0, 1, 2, 1, 0, 255, 0, 0, 2, 3, 2, 1, 0, 255,
0, 0, 3, 2, 0, 255, 255, 255, 0, 0, 0, 255, 255,
]);
});

it('45 degrees binary bilinear', function () {
const image = new Image(
8,
8,
[0x00, 0x00, 0x3c, 0x3c, 0x3c, 0x3c, 0x00, 0x00],
{ kind: 'BINARY' },
);

const result = image.rotate(45, { interpolation: 'bilinear' });
expect(Array.from(result.data)).toStrictEqual([
251, 254, 63, 131, 226, 56, 226, 62, 35, 142, 35, 224, 254, 63, 239, 128,
]);
});

it('30 degrees binary', function () {
const image = new Image(
8,
8,
[0xff, 0xff, 0xc3, 0xdb, 0xdb, 0xc3, 0xff, 0xff],
{ kind: 'BINARY' },
);

const result = image.rotate(30);
expect(Array.from(result.data)).toStrictEqual([
255, 255, 255, 127, 207, 236, 243, 127, 63, 239, 255, 255, 240,
]);
});

it('invalid argument types', function () {
expect(function () {
const image = new Image(
8,
8,
[0xff, 0xff, 0xc3, 0xdb, 0xdb, 0xc3, 0xff, 0xff],
{ kind: 'BINARY' },
);
rotateFree.call(image, '45°');
}).toThrow(/degrees must be a number/);
});
});
59 changes: 50 additions & 9 deletions src/image/transform/crop.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,9 @@ export default function crop(options = {}) {
width = this.width - x,
height = this.height - y,
} = options;

this.checkProcessable('max', {
bitDepth: [8, 16],
this.checkProcessable('crop', {
bitDepth: [1, 8, 16],
});

x = Math.round(x);
y = Math.round(y);
width = Math.round(width);
Expand All @@ -40,36 +38,79 @@ export default function crop(options = {}) {
})`,
);
}

if (width <= 0 || height <= 0) {
throw new RangeError(
`crop: width and height (width:${width}; height:${height}) must be positive numbers`,
);
}

if (x < 0 || y < 0) {
throw new RangeError(
`crop: x and y (x:${x}, y:${y}) must be positive numbers`,
);
}

if (width > this.width - x || height > this.height - y) {
throw new RangeError(
`crop: (x: ${x}, y:${y}, width:${width}, height:${height}) size is out of range`,
);
}

let newImage = Image.createFrom(this, { width, height, position: [x, y] });
let result = this;
if (this.bitDepth === 1) {
const newImage = new Image(width, height, {
kind: 'BINARY',
parent: this,
});
result = cropBinary(this, newImage, x, y, width, height);
} else {
const newImage = Image.createFrom(this, {
width,
height,
position: [x, y],
});
result = cropDefault(this, newImage, x, y, width, height);
}

let xWidth = width * this.channels;
return result;
}

function cropDefault(img, newImage, x, y, width, height) {
let xWidth = width * img.channels;
let y1 = y + height;
let ptr = 0; // pointer for new array

let jLeft = x * img.channels;

for (let i = y; i < y1; i++) {
let j = i * img.width * img.channels + jLeft;
let jL = j + xWidth;

for (; j < jL; j++) {
newImage.data[ptr++] = img.data[j];
}
}

return newImage;
}

function cropBinary(img, newImage, x, y, width, height) {
let xWidth = width * img.channels;
let y1 = y + height;
let ptr = 0; // pointer for new array

let jLeft = x * this.channels;
let jLeft = x * img.channels;

for (let i = y; i < y1; i++) {
let j = i * this.width * this.channels + jLeft;
let j = i * img.width * img.channels + jLeft;
let jL = j + xWidth;

for (; j < jL; j++) {
newImage.data[ptr++] = this.data[j];
if (img.getBit(j)) {
newImage.setBit(ptr);
}
++ptr;
}
}

Expand Down

0 comments on commit 622b909

Please sign in to comment.