Skip to content

Commit

Permalink
undid move of getPalette; added bitmapImage.getRGBASet()
Browse files Browse the repository at this point in the history
  • Loading branch information
jtlapp committed Jan 18, 2018
1 parent 273b307 commit 71d928b
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 130 deletions.
44 changes: 29 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,10 @@ The [Typescript typings](https://github.com/jtlapp/gifwrap/blob/master/index.d.t

* [.fillRGBA(rgba)](#BitmapImage+fillRGBA)

* [.getPalette()](#BitmapImage+getPalette)

* [.getRGBA(x, y)](#BitmapImage+getRGBA)

* [.getRGBASet()](#BitmapImage+getRGBASet)

* [.greyscale()](#BitmapImage+greyscale)

* [.reframe(xOffset, yOffset, width, height, fillRGBA)](#BitmapImage+reframe)
Expand All @@ -230,6 +230,14 @@ The [Typescript typings](https://github.com/jtlapp/gifwrap/blob/master/index.d.t



* [GifFrame](#GifFrame)

* [new GifFrame()](#new_GifFrame_new)

* [.getPalette()](#GifFrame+getPalette)



* [GifUtil](#GifUtil)

* [.cloneFrames(frames)](#GifUtil.cloneFrames)
Expand Down Expand Up @@ -326,18 +334,6 @@ Copy a square portion of this image into another image.
Fills the image with a single color.

**Returns**: [<code>BitmapImage</code>](#BitmapImage) - The present image to allow for chaining.
<a name="BitmapImage+getPalette"></a>

### *bitmapImage*.getPalette()
Get a summary of the colors found within the image. The return value is an object of the following form:

Property | Description
--- | ---
colors | An array of all the opaque colors found within the image. Each color is given as an RGB number of the form 0xRRGGBB. The array is sorted by increasing number. Will be an empty array when the image is completely transparent.
usesTransparency | boolean indicating whether there are any transparent pixels within the image. A pixel is considered transparent if its alpha value is 0x00.
indexCount | The number of color indexes required to represent this palette of colors. It is equal to the number of opaque colors plus one if the image includes transparency.

**Returns**: <code>object</code> - An object representing a color palette as described above.
<a name="BitmapImage+getRGBA"></a>

### *bitmapImage*.getRGBA(x, y)
Expand All @@ -347,9 +343,15 @@ indexCount | The number of color indexes required to represent this palette of c
| x | <code>number</code> | x-coord of pixel |
| y | <code>number</code> | y-coord of pixel |

Gets the RGBA number of the pixel at the given coordinate in the form 0xRRGGBBAA, where AA is the alpha value, with 0x00 being transparent.
Gets the RGBA number of the pixel at the given coordinate in the form 0xRRGGBBAA, where AA is the alpha value, with alpha 0x00 encoding to transparency in GIFs.

**Returns**: <code>number</code> - RGBA of pixel in 0xRRGGBBAA form
<a name="BitmapImage+getRGBASet"></a>

### *bitmapImage*.getRGBASet()
Gets a set of all RGBA colors found within the image.

**Returns**: <code>Set</code> - Set of all RGBA colors that the image contains.
<a name="BitmapImage+greyscale"></a>

### *bitmapImage*.greyscale()
Expand Down Expand Up @@ -429,6 +431,18 @@ See the base class BitmapImage for a discussion of all parameters but `options`

Provide a `frame` to the constructor to create a clone of the provided frame. The new frame includes a copy of the provided frame's pixel data so that each can subsequently be modified without affecting each other.

<a name="GifFrame+getPalette"></a>

### *gifFrame*.getPalette()
Get a summary of the colors found within the frame. The return value is an object of the following form:

Property | Description
--- | ---
colors | An array of all the opaque colors found within the frame. Each color is given as an RGB number of the form 0xRRGGBB. The array is sorted by increasing number. Will be an empty array when the image is completely transparent.
usesTransparency | boolean indicating whether there are any transparent pixels within the frame. A pixel is considered transparent if its alpha value is 0x00.
indexCount | The number of color indexes required to represent this palette of colors. It is equal to the number of opaque colors plus one if the image includes transparency.

**Returns**: <code>object</code> - An object representing a color palette as described above.
<a name="GifUtil.cloneFrames"></a>

### *GifUtil*.cloneFrames(frames)
Expand Down
59 changes: 16 additions & 43 deletions src/bitmapimage.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,49 +138,7 @@ class BitmapImage {
}

/**
* Get a summary of the colors found within the image. The return value is an object of the following form:
*
* Property | Description
* --- | ---
* colors | An array of all the opaque colors found within the image. Each color is given as an RGB number of the form 0xRRGGBB. The array is sorted by increasing number. Will be an empty array when the image is completely transparent.
* usesTransparency | boolean indicating whether there are any transparent pixels within the image. A pixel is considered transparent if its alpha value is 0x00.
* indexCount | The number of color indexes required to represent this palette of colors. It is equal to the number of opaque colors plus one if the image includes transparency.
*
* @return {object} An object representing a color palette as described above.
*/

getPalette() {
// returns with colors sorted low to high
const colorSet = new Set();
const buf = this.bitmap.data;
let i = 0;
let usesTransparency = false;
while (i < buf.length) {
if (buf[i + 3] === 0) {
usesTransparency = true;
}
else {
// can eliminate the bitshift by starting one byte prior
const color = (buf.readUInt32BE(i, true) >> 8) & 0xFFFFFF;
colorSet.add(color);
}
i += 4; // skip alpha
}
const colors = new Array(colorSet.size);
const iter = colorSet.values();
for (i = 0; i < colors.length; ++i) {
colors[i] = iter.next().value;
}
colors.sort((a, b) => (a - b));
let indexCount = colors.length;
if (usesTransparency) {
++indexCount;
}
return { colors, usesTransparency, indexCount };
}

/**
* Gets the RGBA number of the pixel at the given coordinate in the form 0xRRGGBBAA, where AA is the alpha value, with 0x00 being transparent.
* Gets the RGBA number of the pixel at the given coordinate in the form 0xRRGGBBAA, where AA is the alpha value, with alpha 0x00 encoding to transparency in GIFs.
*
* @param {number} x x-coord of pixel
* @param {number} y y-coord of pixel
Expand All @@ -192,6 +150,21 @@ class BitmapImage {
return this.bitmap.data.readUInt32BE(bi);
}

/**
* Gets a set of all RGBA colors found within the image.
*
* @return {Set} Set of all RGBA colors that the image contains.
*/

getRGBASet() {
const rgbaSet = new Set();
const buf = this.bitmap.data;
for (let bi = 0; bi < buf.length; bi += 4) {
rgbaSet.add(buf.readUInt32BE(bi, true));
}
return rgbaSet;
}

/**
* Converts the image to greyscale using inferred Adobe metrics.
*
Expand Down
42 changes: 42 additions & 0 deletions src/gifframe.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,48 @@ class GifFrame extends BitmapImage {
this.interlaced = options.interlaced || false;
}
}

/**
* Get a summary of the colors found within the frame. The return value is an object of the following form:
*
* Property | Description
* --- | ---
* colors | An array of all the opaque colors found within the frame. Each color is given as an RGB number of the form 0xRRGGBB. The array is sorted by increasing number. Will be an empty array when the image is completely transparent.
* usesTransparency | boolean indicating whether there are any transparent pixels within the frame. A pixel is considered transparent if its alpha value is 0x00.
* indexCount | The number of color indexes required to represent this palette of colors. It is equal to the number of opaque colors plus one if the image includes transparency.
*
* @return {object} An object representing a color palette as described above.
*/

getPalette() {
// returns with colors sorted low to high
const colorSet = new Set();
const buf = this.bitmap.data;
let i = 0;
let usesTransparency = false;
while (i < buf.length) {
if (buf[i + 3] === 0) {
usesTransparency = true;
}
else {
// can eliminate the bitshift by starting one byte prior
const color = (buf.readUInt32BE(i, true) >> 8) & 0xFFFFFF;
colorSet.add(color);
}
i += 4; // skip alpha
}
const colors = new Array(colorSet.size);
const iter = colorSet.values();
for (i = 0; i < colors.length; ++i) {
colors[i] = iter.next().value;
}
colors.sort((a, b) => (a - b));
let indexCount = colors.length;
if (usesTransparency) {
++indexCount;
}
return { colors, usesTransparency, indexCount };
}
}

GifFrame.DisposeToAnything = 0;
Expand Down
65 changes: 0 additions & 65 deletions test/test_bitmap.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,71 +92,6 @@ describe("GifFrame bad construction behavior", () => {
});
});

describe("BitmapImage palette", () => {

it("is monocolor without transparency", (done) => {

const bitmap = Tools.getBitmap('singleFrameMonoOpaque');
const f = new BitmapImage(bitmap);
const p = f.getPalette();
assert.deepStrictEqual(p.colors, [0xFF0000]);
assert.strictEqual(p.usesTransparency, false);
done();
});

it("includes two colors without transparency", (done) => {

const bitmap = Tools.getBitmap('singleFrameBWOpaque');
const f = new BitmapImage(bitmap);
const p = f.getPalette();
assert.deepStrictEqual(p.colors, [0x000000, 0xffffff]);
assert.strictEqual(p.usesTransparency, false);
done();
});

it("includes multiple colors without transparency", (done) => {

const bitmap = Tools.getBitmap('singleFrameMultiOpaque');
const f = new BitmapImage(bitmap);
const p = f.getPalette();
assert.deepStrictEqual(p.colors,
[0x0000ff, 0x00ff00, 0xff0000, 0xffffff]);
assert.strictEqual(p.usesTransparency, false);
done();
});

it("has only transparency", (done) => {

const bitmap = Tools.getBitmap('singleFrameNoColorTrans');
const f = new BitmapImage(bitmap);
const p = f.getPalette();
assert.deepStrictEqual(p.colors, []);
assert.strictEqual(p.usesTransparency, true);
done();
});

it("is monocolor with transparency", (done) => {

const bitmap = Tools.getBitmap('singleFrameMonoTrans');
const f = new BitmapImage(bitmap);
const p = f.getPalette();
assert.deepStrictEqual(p.colors, [0x00ff00]);
assert.strictEqual(p.usesTransparency, true);
done();
});

it("includes multiple colors with transparency", (done) => {

const bitmap = Tools.getBitmap('singleFrameMultiPartialTrans');
const f = new BitmapImage(bitmap);
const p = f.getPalette();
assert.deepStrictEqual(p.colors,
[0x000000, 0x0000ff, 0x00ff00, 0xff0000, 0xffffff]);
assert.strictEqual(p.usesTransparency, true);
done();
});
});

// TBD: test BitmapImage transformation methods

describe("Jimp compatibility", () => {
Expand Down
65 changes: 65 additions & 0 deletions test/test_frame.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,71 @@ describe("GifFrame bad construction behavior", () => {
});
});

describe("GifFrame palette", () => {

it("is monocolor without transparency", (done) => {

const bitmap = Tools.getBitmap('singleFrameMonoOpaque');
const f = new GifFrame(bitmap);
const p = f.getPalette();
assert.deepStrictEqual(p.colors, [0xFF0000]);
assert.strictEqual(p.usesTransparency, false);
done();
});

it("includes two colors without transparency", (done) => {

const bitmap = Tools.getBitmap('singleFrameBWOpaque');
const f = new GifFrame(bitmap);
const p = f.getPalette();
assert.deepStrictEqual(p.colors, [0x000000, 0xffffff]);
assert.strictEqual(p.usesTransparency, false);
done();
});

it("includes multiple colors without transparency", (done) => {

const bitmap = Tools.getBitmap('singleFrameMultiOpaque');
const f = new GifFrame(bitmap);
const p = f.getPalette();
assert.deepStrictEqual(p.colors,
[0x0000ff, 0x00ff00, 0xff0000, 0xffffff]);
assert.strictEqual(p.usesTransparency, false);
done();
});

it("has only transparency", (done) => {

const bitmap = Tools.getBitmap('singleFrameNoColorTrans');
const f = new GifFrame(bitmap);
const p = f.getPalette();
assert.deepStrictEqual(p.colors, []);
assert.strictEqual(p.usesTransparency, true);
done();
});

it("is monocolor with transparency", (done) => {

const bitmap = Tools.getBitmap('singleFrameMonoTrans');
const f = new GifFrame(bitmap);
const p = f.getPalette();
assert.deepStrictEqual(p.colors, [0x00ff00]);
assert.strictEqual(p.usesTransparency, true);
done();
});

it("includes multiple colors with transparency", (done) => {

const bitmap = Tools.getBitmap('singleFrameMultiPartialTrans');
const f = new GifFrame(bitmap);
const p = f.getPalette();
assert.deepStrictEqual(p.colors,
[0x000000, 0x0000ff, 0x00ff00, 0xff0000, 0xffffff]);
assert.strictEqual(p.usesTransparency, true);
done();
});
});

function _assertDefaultFrameOptions(frame) {
assert.strictEqual(frame.xOffset, 0);
assert.strictEqual(frame.yOffset, 0);
Expand Down
22 changes: 15 additions & 7 deletions test/test_quantize.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ describe("graphics color index reduction", () => {
return _graphicsTest('quantizeDekker', 256);
});


it("Sorokin-quantizes down to 32 colors", () => {

return _graphicsTest('quantizeSorokin', 32);
Expand Down Expand Up @@ -146,6 +145,15 @@ function _graphicsTest(method, maxColors) {
})
}

function _hasTransparency(colorSet) {
for (let rgba of colorSet.values()) {
if ((rgba & 0xff) === 0x00) {
return true;
}
}
return false;
}

function _photoTest(method, maxColors) {
return _reductionTest("hairstreak.jpg", false, method, maxColors);
}
Expand All @@ -171,9 +179,9 @@ function _reductionTest(sourceFile, usesTransparency, method, maxColors, modifie
if (err) return reject(err);
work = new BitmapImage(manyJimp.bitmap);

const inputPalette = work.getPalette();
assert.strictEqual(inputPalette.usesTransparency, usesTransparency, label);
assert.isAtLeast(inputPalette.indexCount, maxColors + 1, label);
const inputColorSet = work.getRGBASet();
assert.strictEqual(_hasTransparency(inputColorSet), usesTransparency, label);
assert.isAtLeast(inputColorSet.size, maxColors + 1, label);

if (method === 'quantizeDekker') {
GifUtil[method](work, maxColors, dither);
Expand All @@ -187,9 +195,9 @@ function _reductionTest(sourceFile, usesTransparency, method, maxColors, modifie
assert.strictEqual(workBuf.length, manyJimp.bitmap.data.length, label);
assert.strictEqual(workBuf.compare(limitedBuf), 0, label);

const outputPalette = work.getPalette();
assert.strictEqual(outputPalette.usesTransparency, usesTransparency, label);
assert.isAtMost(outputPalette.indexCount, maxColors, label);
const outputColorSet = work.getRGBASet();
assert.strictEqual(_hasTransparency(outputColorSet), usesTransparency, label);
assert.isAtMost(outputColorSet.size, maxColors, label);
resolve();
});
});
Expand Down

0 comments on commit 71d928b

Please sign in to comment.