yahoo/pngjs-image

View on GitHub
test/tests.js

Summary

Maintainability
F
3 wks
Test Coverage
// Copyright 2014-2015 Yahoo! Inc.
// Copyrights licensed under the Mit License. See the accompanying LICENSE file for terms.

var PNG = require('pngjs').PNG;
var PNGImage = require('../index');
var expect = require('chai').expect;
var fs = require('fs');

/**
 * Generates an image
 *
 * @param {Buffer} blob
 * @returns {PNGImage}
 */
function generateImage (blob) {

    var png = new PNG({
        width: 2, height: 3
    });

    png.data = blob;

    return new PNGImage(png);
}

/**
 * Generates a blob
 *
 * @returns {Buffer}
 */
function generateBlob () {

    var buffer = new Buffer(2 * 3 * 4);

    buffer[0] = 18; // Red
    buffer[1] = 22; // Green
    buffer[2] = 37; // Blue
    buffer[3] = 42; // Alpha

    buffer[4] = 57; // Red
    buffer[5] = 65; // Green
    buffer[6] = 79; // Blue
    buffer[7] = 81; // Alpha

    buffer[8] = 97; // Red
    buffer[9] = 130; // Green
    buffer[10] = 118; // Blue
    buffer[11] = 124; // Alpha

    buffer[12] = 131; // Red
    buffer[13] = 147; // Green
    buffer[14] = 153; // Blue
    buffer[15] = 169; // Alpha

    buffer[16] = 92; // Red
    buffer[17] = 90; // Green
    buffer[18] = 13; // Blue
    buffer[19] = 184; // Alpha

    buffer[20] = 111; // Red
    buffer[21] = 14; // Green
    buffer[22] = 13; // Blue
    buffer[23] = 69; // Alpha

    return buffer;
}

/**
 * Compares the buffer contents of two buffers
 *
 * @param {Buffer} bufferActual
 * @param {Buffer} bufferExpected
 * @param {int} actualOffset
 * @param {int} expectedOffset
 * @param {int} length
 */
function compareBuffers (bufferActual, bufferExpected, actualOffset, expectedOffset, length) {
    for (var i = 0; i < length; i++) {
        expect(bufferActual[actualOffset + i]).to.be.equal(bufferExpected[expectedOffset + i]);
    }
}

describe('Filter', function () {

    it('should have the blur filter', function () {
        expect(PNGImage.filters).to.contain.key("blur");
    });

    it('should have the grayScale filter', function () {
        expect(PNGImage.filters).to.contain.key("grayScale");
    });

    it('should have the lightness filter', function () {
        expect(PNGImage.filters).to.contain.key("lightness");
    });

    it('should have the luma filter', function () {
        expect(PNGImage.filters).to.contain.key("luma");
    });

    it('should have the luminosity filter', function () {
        expect(PNGImage.filters).to.contain.key("luminosity");
    });

    it('should have the sepia filter', function () {
        expect(PNGImage.filters).to.contain.key("sepia");
    });

    it('should add a new filter', function () {
        PNGImage.setFilter('test', function () {
        });
        expect(PNGImage.filters).to.contain.key("test");
    });

    it('should remove a filter', function () {
        PNGImage.setFilter('test');
        expect(PNGImage.filters).to.not.contain.key("test");
    });
});

describe('Instance', function () {

    beforeEach(function () {
        this.blobCopy = generateBlob();
        this.blob = generateBlob();
        this.instance = generateImage(this.blob);
    });

    describe('Default methods', function () {

        it('should return the initial image', function () {
            expect(this.instance.getImage()).to.be.instanceof(PNG);
            expect(this.instance.getImage().data).to.be.equal(this.blob);
        });

        it('should return the initial blob', function () {
            expect(this.instance.getBlob()).to.be.equal(this.blob);
        });

        it('should return the width', function () {
            expect(this.instance.getWidth()).to.be.equal(2);
        });

        it('should return the height', function () {
            expect(this.instance.getHeight()).to.be.equal(3);
        });

        describe('getIndex', function () {

            it('should get the first index from zero coordinates', function () {
                expect(this.instance.getIndex(0, 0)).to.be.equal(0);
            });

            it('should get the index of second column', function () {
                expect(this.instance.getIndex(1, 0)).to.be.equal(1);
            });

            it('should get the index of second row', function () {
                expect(this.instance.getIndex(0, 1)).to.be.equal(2);
            });

            it('should get the last index from 1/2 coordinates', function () {
                expect(this.instance.getIndex(1, 2)).to.be.equal(5);
            });
        });

        describe('clip', function () {

            it('should not clip same size images', function () {

                this.instance.clip(0, 0, 2, 3);

                expect(this.instance.getBlob().length, this.blobCopy.length);
                compareBuffers(this.instance.getBlob(), this.blobCopy, 0, 0, this.blobCopy.length);
            });

            describe('zero offset', function () {

                it('should clip image', function () {

                    this.instance.clip(0, 0, 2, 2);

                    expect(this.instance.getBlob().length, 2 * 2 * 4);
                });

                it('should copy image when clipping', function () {

                    this.instance.clip(0, 0, 2, 2);

                    compareBuffers(this.instance.getBlob(), this.blobCopy, 0, 0, this.instance.getBlob().length);
                });
            });

            describe('with offset', function () {

                it('should clip image', function () {

                    this.instance.clip(0, 1, 2, 2);

                    expect(this.instance.getBlob().length, 2 * 2 * 4);
                });

                it('should copy image when clipping', function () {

                    this.instance.clip(0, 1, 2, 2);

                    compareBuffers(this.instance.getBlob(), this.blobCopy, 0, 8, this.instance.getBlob().length);
                });
            });
        });

        describe('fillRect', function () {

            it('should fill same size images', function () {
                var blob;

                this.instance.fillRect(0, 0, 2, 3, {red: 1, green: 2, blue: 3, alpha: 4});

                blob = this.instance.getBlob();
                for (var i = 0; i < 2 * 3 * 4; i += 4) {
                    expect(blob[i]).to.be.equal(1);
                    expect(blob[i + 1]).to.be.equal(2);
                    expect(blob[i + 2]).to.be.equal(3);
                    expect(blob[i + 3]).to.be.equal(4);
                }
            });

            it('should fill upper image', function () {
                var blob;

                this.instance.fillRect(0, 0, 2, 2, {red: 1, green: 2, blue: 3, alpha: 4});

                blob = this.instance.getBlob();
                for (var i = 0; i < 2 * 2 * 4; i += 4) {
                    expect(blob[i]).to.be.equal(1);
                    expect(blob[i + 1]).to.be.equal(2);
                    expect(blob[i + 2]).to.be.equal(3);
                    expect(blob[i + 3]).to.be.equal(4);
                }

                // Make sure that the last row is still the old image
                compareBuffers(this.instance.getBlob(), this.blobCopy, 16, 16, 8);
            });

            it('should fill lower image', function () {
                var blob;

                this.instance.fillRect(0, 1, 2, 2, {red: 1, green: 2, blue: 3, alpha: 4});

                blob = this.instance.getBlob();
                for (var i = 8; i < 2 * 2 * 4; i += 4) {
                    expect(blob[i]).to.be.equal(1);
                    expect(blob[i + 1]).to.be.equal(2);
                    expect(blob[i + 2]).to.be.equal(3);
                    expect(blob[i + 3]).to.be.equal(4);
                }

                // Make sure that the first row is still the old image
                compareBuffers(this.instance.getBlob(), this.blobCopy, 0, 0, 8);
            });
        });

        it('should write to a file', function (done) {

            var path = __dirname + '/tmp.png';

            this.instance.writeImage(path, function (err) {

                var contentsActual, contentsExpected;

                if (err) {
                    done(err);
                } else {

                    try {
                        if (fs.existsSync(path)) {
                            contentsActual = fs.readFileSync(path).toString();
                            contentsExpected = fs.readFileSync(__dirname + '/test.png').toString();
                            expect(contentsActual).to.be.equal(contentsExpected);
                        }

                        done();
                    } catch (err) {
                        done(err);
                    }
                }
            });
        });

        it('should get blob', function (done) {

            this.instance.toBlob(function (err, contentsActual) {

                var contentsExpected;

                if (err) {
                    done(err);
                } else {

                    try {
                        contentsExpected = fs.readFileSync(__dirname + '/test.png').toString();
                        expect(contentsActual.toString()).to.be.equal(contentsExpected);

                        done();
                    } catch (err) {
                        done(err);
                    }
                }
            });
        });
    });

    describe('Statics', function () {

        it('should copy the image', function () {
            var copy = PNGImage.copyImage(this.instance);
            expect(this.instance.getBlob().length, copy.getBlob().length);
            compareBuffers(this.instance.getBlob(), copy.getBlob(), 0, 0, copy.getBlob().length);
        });

        it('should read an image', function (done) {

            PNGImage.readImage(__dirname + '/test.png', function (err, image) {

                if (err) {
                    done(err);
                } else {

                    try {
                        expect(image.getBlob().length, this.instance.getBlob().length);
                        compareBuffers(image.getBlob(), this.instance.getBlob(), 0, 0, image.getBlob().length);

                        done();
                    } catch (err) {
                        done(err);
                    }
                }

            }.bind(this));
        });

        it('should error on non-existent image', function (done) {
            PNGImage.readImage(__dirname + '/nonexistent.png', function (err, image) {
                expect(err).to.not.be.undefined;
                done();
            });
        });

        it('should load an image', function (done) {

            var contents = fs.readFileSync(__dirname + '/test.png');

            PNGImage.loadImage(contents, function (err, image) {

                if (err) {
                    done(err);
                } else {

                    try {
                        expect(image.getBlob().length, this.instance.getBlob().length);
                        compareBuffers(image.getBlob(), this.instance.getBlob(), 0, 0, image.getBlob().length);

                        done();
                    } catch (err) {
                        done(err);
                    }
                }

            }.bind(this));
        });
    });

    describe('Pixel manipulation', function () {

        describe('color-value', function () {

            it('should return value from first index', function () {
                expect(this.instance._getValue(0, 1)).to.be.equal(this.blob[1]);
            });

            it('should return value from second index', function () {
                expect(this.instance._getValue(3, 0)).to.be.equal(this.blob[3 << 2]);
            });

            it('should set the value of first index', function () {
                this.instance._setValue(0, 1, 23);
                expect(this.blob[1]).to.be.equal(23);
            });

            it('should set the value of second index', function () {
                this.instance._setValue(4, 2, 47);
                expect(this.blob[(4 << 2) + 2]).to.be.equal(47);
            });

            it('should set the value with opacity', function () {
                this.instance._setValue(0, 1, 47, 0.5);
                expect(this.blob[1]).to.be.equal(Math.floor(this.blobCopy[1] * 0.5 + 47 * 0.5));
            });
        });

        describe('red', function () {

            beforeEach(function () {
                this.offset = 2 * 4 + 0;
            });

            it('should get the value', function () {
                expect(this.instance.getRed(2)).to.be.equal(this.blob[this.offset]);
            });

            it('should set the value', function () {
                this.instance.setRed(2, 23);
                expect(this.blob[this.offset]).to.be.equal(23);
            });

            it('should set the value with opacity', function () {
                this.instance.setRed(2, 23, 0.5);
                expect(this.blob[this.offset]).to.be.equal(Math.floor(this.blobCopy[this.offset] * 0.5 + 23 * 0.5));
            });
        });

        describe('green', function () {

            beforeEach(function () {
                this.offset = 2 * 4 + 1;
            });

            it('should get the value', function () {
                expect(this.instance.getGreen(2)).to.be.equal(this.blob[this.offset]);
            });

            it('should set the value', function () {
                this.instance.setGreen(2, 23);
                expect(this.blob[this.offset]).to.be.equal(23);
            });

            it('should set the value with opacity', function () {
                this.instance.setGreen(2, 23, 0.5);
                expect(this.blob[this.offset]).to.be.equal(Math.floor(this.blobCopy[this.offset] * 0.5 + 23 * 0.5));
            });
        });

        describe('blue', function () {

            beforeEach(function () {
                this.offset = 2 * 4 + 2;
            });

            it('should get the value', function () {
                expect(this.instance.getBlue(2)).to.be.equal(this.blob[this.offset]);
            });

            it('should set the value', function () {
                this.instance.setBlue(2, 23);
                expect(this.blob[this.offset]).to.be.equal(23);
            });

            it('should set the value with opacity', function () {
                this.instance.setBlue(2, 23, 0.5);
                expect(this.blob[this.offset]).to.be.equal(Math.floor(this.blobCopy[this.offset] * 0.5 + 23 * 0.5));
            });
        });

        describe('alpha', function () {

            beforeEach(function () {
                this.offset = 2 * 4 + 3;
            });

            it('should get the value', function () {
                expect(this.instance.getAlpha(2)).to.be.equal(this.blob[this.offset]);
            });

            it('should set the value', function () {
                this.instance.setAlpha(2, 23);
                expect(this.blob[this.offset]).to.be.equal(23);
            });

            it('should set the value with opacity', function () {
                this.instance.setAlpha(2, 23, 0.5);
                expect(this.blob[this.offset]).to.be.equal(Math.floor(this.blobCopy[this.offset] * 0.5 + 23 * 0.5));
            });
        });

        describe('setAtIndex', function () {

            it('should set red color', function () {
                this.instance.setAtIndex(0, {red: 1});
                expect(this.blob[0]).to.be.equal(1);
            });

            it('should set red color with opacity', function () {
                this.instance.setAtIndex(0, {red: 1, opacity: 0.5});
                expect(this.blob[0]).to.be.equal(9);
            });

            it('should set green color', function () {
                this.instance.setAtIndex(1, {green: 2});
                expect(this.blob[5]).to.be.equal(2);
            });

            it('should set green color with opacity', function () {
                this.instance.setAtIndex(1, {green: 2, opacity: 0.5});
                expect(this.blob[5]).to.be.equal(33);
            });

            it('should set blue color', function () {
                this.instance.setAtIndex(0, {blue: 3});
                expect(this.blob[2]).to.be.equal(3);
            });

            it('should set blue color with opacity', function () {
                this.instance.setAtIndex(0, {blue: 3, opacity: 0.5});
                expect(this.blob[2]).to.be.equal(20);
            });

            it('should set alpha value', function () {
                this.instance.setAtIndex(0, {alpha: 4});
                expect(this.blob[3]).to.be.equal(4);
            });

            it('should set alpha value with opacity', function () {
                this.instance.setAtIndex(0, {alpha: 4, opacity: 0.5});
                expect(this.blob[3]).to.be.equal(23);
            });

            it('should ignore colors if none given', function () {
                this.instance.setAtIndex(0, {});
                expect(this.blob[0]).to.be.equal(this.blobCopy[0]);
                expect(this.blob[1]).to.be.equal(this.blobCopy[1]);
                expect(this.blob[2]).to.be.equal(this.blobCopy[2]);
                expect(this.blob[3]).to.be.equal(this.blobCopy[3]);
            });
        });

        describe('set color', function () {

            it('should set value with setAt', function () {
                this.instance.setAt(0, 1, {red: 1, green: 2, blue: 3, alpha: 4});
                expect(this.blob[8]).to.be.equal(1);
                expect(this.blob[9]).to.be.equal(2);
                expect(this.blob[10]).to.be.equal(3);
                expect(this.blob[11]).to.be.equal(4);
            });

            it('should set value with setPixel', function () {
                this.instance.setPixel(0, 1, {red: 1, green: 2, blue: 3, alpha: 4});
                expect(this.blob[8]).to.be.equal(1);
                expect(this.blob[9]).to.be.equal(2);
                expect(this.blob[10]).to.be.equal(3);
                expect(this.blob[11]).to.be.equal(4);
            });
        });

        describe('get color', function () {

            it('should get the color with getColorAtIndex', function () {
                expect(this.instance.getColorAtIndex(1)).to.be.equal(5194041);
            });

            it('should get the color with getColor', function () {
                expect(this.instance.getColor(0, 1)).to.be.equal(7766625);
            });

            it('should get the color with getAtIndex', function () {
                expect(this.instance.getAtIndex(1)).to.be.equal(1364148537);
            });

            it('should get the color with getAt', function () {
                expect(this.instance.getAt(0, 1)).to.be.equal(2088141409);
            });

            it('should get the color with getPixel', function () {
                expect(this.instance.getPixel(0, 1)).to.be.equal(2088141409);
            });
        });

        describe('_calculateColorValue', function () {

            it('should use paint-color when no opacity given', function () {
                expect(this.instance._calculateColorValue(1, 2)).to.be.equal(2);
            });

            it('should apply full color with full opacity', function () {
                expect(this.instance._calculateColorValue(100, 200, 1)).to.be.equal(200);
            });

            it('should apply non of the color with zero opacity', function () {
                expect(this.instance._calculateColorValue(100, 200, 0)).to.be.equal(100);
            });

            it('should apply the color with half opacity', function () {
                expect(this.instance._calculateColorValue(100, 200, 0.5)).to.be.equal(150);
            });

            it('should apply the color with 80% opacity', function () {
                expect(this.instance._calculateColorValue(100, 200, 0.8)).to.be.equal(180);
            });

            it('should apply the color with fraction opacity', function () {
                expect(this.instance._calculateColorValue(100, 200, 1 / 3)).to.be.equal(133);
            });
        });
    });

    describe('conversion', function () {

        describe('blur', function () {

            it('should give the value of a coordinate with default grayscale function', function () {
                expect(this.instance.getBlurPixel(0, 1)).to.be.equal(78);
            });

            it('should give the value of a coordinate with luminosity', function () {
                expect(this.instance.getBlurPixel(1, 0, 'getLuminosityAtIndex')).to.be.equal(88);
            });

            it('should give the value of a coordinate with luma', function () {
                expect(this.instance.getBlurPixel(1, 2, 'getLuminosityAtIndex')).to.be.equal(96);
            });

            it('should give the value of an index', function () {
                expect(this.instance.getBlurPixelAtIndex(1)).to.be.equal(88);
            });
        });

        describe('YIQ', function () {

            it('should give the value of a coordinate', function () {
                expect(this.instance.getYIQ(0, 1)).to.be.deep.equal({y: 118, i: 0, q: 0});
            });

            it('should give the value of an index', function () {
                expect(this.instance.getYIQAtIndex(1)).to.be.deep.equal({y: 64, i: 0, q: 2});
            });
        });

        describe('luma', function () {

            it('should give the value of a coordinate', function () {
                expect(this.instance.getLuma(0, 1)).to.be.equal(118);
            });

            it('should give the value of an index', function () {
                expect(this.instance.getLumaAtIndex(1)).to.be.equal(64);
            });
        });

        describe('sepia', function () {

            it('should give the value of a coordinate', function () {
                expect(this.instance.getSepia(0, 1)).to.be.deep.equal({red: 160, green: 164, blue: 146});
            });

            it('should give the value of an index', function () {
                expect(this.instance.getSepiaAtIndex(1)).to.be.deep.equal({red: 87, green: 88, blue: 81});
            });
        });

        describe('luminosity', function () {

            it('should give the value of a coordinate', function () {
                expect(this.instance.getLuminosity(0, 1)).to.be.equal(122);
            });

            it('should give the value of an index', function () {
                expect(this.instance.getLuminosityAtIndex(1)).to.be.equal(64);
            });
        });

        describe('lightness', function () {

            it('should give the value of a coordinate', function () {
                expect(this.instance.getLightness(0, 1)).to.be.equal(113);
            });

            it('should give the value of an index', function () {
                expect(this.instance.getLightnessAtIndex(1)).to.be.equal(68);
            });
        });

        describe('grayScale', function () {

            it('should give the value of a coordinate', function () {
                expect(this.instance.getGrayScale(0, 1)).to.be.equal(115);
            });

            it('should give the value of an index', function () {
                expect(this.instance.getGrayScaleAtIndex(1)).to.be.equal(67);
            });
        });
    });

    describe('filter', function () {

        it('should apply blur filter', function () {
            this.instance.applyFilters("blur");
            expect(this.instance.getRed(0)).to.be.equal(88);
        });

        it('should apply grayScale filter', function () {
            this.instance.applyFilters("grayScale");
            expect(this.instance.getRed(0)).to.be.equal(25);
        });

        it('should apply lightness filter', function () {
            this.instance.applyFilters("lightness");
            expect(this.instance.getRed(0)).to.be.equal(27);
        });

        it('should apply luma filter', function () {
            this.instance.applyFilters("luma");
            expect(this.instance.getRed(0)).to.be.equal(22);
        });

        it('should apply luminosity filter', function () {
            this.instance.applyFilters("luminosity");
            expect(this.instance.getRed(0)).to.be.equal(22);
        });

        it('should apply sepia filter', function () {
            this.instance.applyFilters("sepia");
            expect(this.instance.getRed(0)).to.be.equal(30);
        });

        it('should apply object filter', function () {
            this.instance.applyFilters({key: "blur", options: {funcName: "getGrayScaleAtIndex"}});
            expect(this.instance.getRed(0)).to.be.equal(87);
        });

        it('should apply multiple filters', function () {
            this.instance.applyFilters(["luminosity", "sepia"]);
            expect(this.instance.getRed(0)).to.be.equal(29);
        });

        it('should apply multiple filters with different values', function () {
            this.instance.applyFilters(["luminosity", {key: "blur", options: {funcName: "getGrayScaleAtIndex"}}]);
            expect(this.instance.getRed(0)).to.be.equal(88);
        });

        it('should apply multiple filters and ignore unknown', function () {
            this.instance.applyFilters(["luminosity", undefined]);
            expect(this.instance.getRed(0)).to.be.equal(22);
        });

        it('should apply blur filter without modifying image', function () {
            var newImage = this.instance.applyFilters("blur", true);
            expect(this.instance.getRed(0)).to.be.equal(18);
            expect(newImage.getRed(0)).to.be.equal(88);
        });
    });
});