Skip to content

Commit 71d928b

Browse files
committed
undid move of getPalette; added bitmapImage.getRGBASet()
1 parent 273b307 commit 71d928b

File tree

6 files changed

+167
-130
lines changed

6 files changed

+167
-130
lines changed

README.md

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -214,10 +214,10 @@ The [Typescript typings](https://github.com/jtlapp/gifwrap/blob/master/index.d.t
214214

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

217-
* [.getPalette()](#BitmapImage+getPalette)
218-
219217
* [.getRGBA(x, y)](#BitmapImage+getRGBA)
220218

219+
* [.getRGBASet()](#BitmapImage+getRGBASet)
220+
221221
* [.greyscale()](#BitmapImage+greyscale)
222222

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

231231

232232

233+
* [GifFrame](#GifFrame)
234+
235+
* [new GifFrame()](#new_GifFrame_new)
236+
237+
* [.getPalette()](#GifFrame+getPalette)
238+
239+
240+
233241
* [GifUtil](#GifUtil)
234242

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

328336
**Returns**: [<code>BitmapImage</code>](#BitmapImage) - The present image to allow for chaining.
329-
<a name="BitmapImage+getPalette"></a>
330-
331-
### *bitmapImage*.getPalette()
332-
Get a summary of the colors found within the image. The return value is an object of the following form:
333-
334-
Property | Description
335-
--- | ---
336-
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.
337-
usesTransparency | boolean indicating whether there are any transparent pixels within the image. A pixel is considered transparent if its alpha value is 0x00.
338-
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.
339-
340-
**Returns**: <code>object</code> - An object representing a color palette as described above.
341337
<a name="BitmapImage+getRGBA"></a>
342338

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

350-
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.
346+
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.
351347

352348
**Returns**: <code>number</code> - RGBA of pixel in 0xRRGGBBAA form
349+
<a name="BitmapImage+getRGBASet"></a>
350+
351+
### *bitmapImage*.getRGBASet()
352+
Gets a set of all RGBA colors found within the image.
353+
354+
**Returns**: <code>Set</code> - Set of all RGBA colors that the image contains.
353355
<a name="BitmapImage+greyscale"></a>
354356

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

430432
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.
431433

434+
<a name="GifFrame+getPalette"></a>
435+
436+
### *gifFrame*.getPalette()
437+
Get a summary of the colors found within the frame. The return value is an object of the following form:
438+
439+
Property | Description
440+
--- | ---
441+
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.
442+
usesTransparency | boolean indicating whether there are any transparent pixels within the frame. A pixel is considered transparent if its alpha value is 0x00.
443+
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.
444+
445+
**Returns**: <code>object</code> - An object representing a color palette as described above.
432446
<a name="GifUtil.cloneFrames"></a>
433447

434448
### *GifUtil*.cloneFrames(frames)

src/bitmapimage.js

Lines changed: 16 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -138,49 +138,7 @@ class BitmapImage {
138138
}
139139

140140
/**
141-
* Get a summary of the colors found within the image. The return value is an object of the following form:
142-
*
143-
* Property | Description
144-
* --- | ---
145-
* 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.
146-
* usesTransparency | boolean indicating whether there are any transparent pixels within the image. A pixel is considered transparent if its alpha value is 0x00.
147-
* 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.
148-
*
149-
* @return {object} An object representing a color palette as described above.
150-
*/
151-
152-
getPalette() {
153-
// returns with colors sorted low to high
154-
const colorSet = new Set();
155-
const buf = this.bitmap.data;
156-
let i = 0;
157-
let usesTransparency = false;
158-
while (i < buf.length) {
159-
if (buf[i + 3] === 0) {
160-
usesTransparency = true;
161-
}
162-
else {
163-
// can eliminate the bitshift by starting one byte prior
164-
const color = (buf.readUInt32BE(i, true) >> 8) & 0xFFFFFF;
165-
colorSet.add(color);
166-
}
167-
i += 4; // skip alpha
168-
}
169-
const colors = new Array(colorSet.size);
170-
const iter = colorSet.values();
171-
for (i = 0; i < colors.length; ++i) {
172-
colors[i] = iter.next().value;
173-
}
174-
colors.sort((a, b) => (a - b));
175-
let indexCount = colors.length;
176-
if (usesTransparency) {
177-
++indexCount;
178-
}
179-
return { colors, usesTransparency, indexCount };
180-
}
181-
182-
/**
183-
* 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.
141+
* 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.
184142
*
185143
* @param {number} x x-coord of pixel
186144
* @param {number} y y-coord of pixel
@@ -192,6 +150,21 @@ class BitmapImage {
192150
return this.bitmap.data.readUInt32BE(bi);
193151
}
194152

153+
/**
154+
* Gets a set of all RGBA colors found within the image.
155+
*
156+
* @return {Set} Set of all RGBA colors that the image contains.
157+
*/
158+
159+
getRGBASet() {
160+
const rgbaSet = new Set();
161+
const buf = this.bitmap.data;
162+
for (let bi = 0; bi < buf.length; bi += 4) {
163+
rgbaSet.add(buf.readUInt32BE(bi, true));
164+
}
165+
return rgbaSet;
166+
}
167+
195168
/**
196169
* Converts the image to greyscale using inferred Adobe metrics.
197170
*

src/gifframe.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,48 @@ class GifFrame extends BitmapImage {
6262
this.interlaced = options.interlaced || false;
6363
}
6464
}
65+
66+
/**
67+
* Get a summary of the colors found within the frame. The return value is an object of the following form:
68+
*
69+
* Property | Description
70+
* --- | ---
71+
* 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.
72+
* usesTransparency | boolean indicating whether there are any transparent pixels within the frame. A pixel is considered transparent if its alpha value is 0x00.
73+
* 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.
74+
*
75+
* @return {object} An object representing a color palette as described above.
76+
*/
77+
78+
getPalette() {
79+
// returns with colors sorted low to high
80+
const colorSet = new Set();
81+
const buf = this.bitmap.data;
82+
let i = 0;
83+
let usesTransparency = false;
84+
while (i < buf.length) {
85+
if (buf[i + 3] === 0) {
86+
usesTransparency = true;
87+
}
88+
else {
89+
// can eliminate the bitshift by starting one byte prior
90+
const color = (buf.readUInt32BE(i, true) >> 8) & 0xFFFFFF;
91+
colorSet.add(color);
92+
}
93+
i += 4; // skip alpha
94+
}
95+
const colors = new Array(colorSet.size);
96+
const iter = colorSet.values();
97+
for (i = 0; i < colors.length; ++i) {
98+
colors[i] = iter.next().value;
99+
}
100+
colors.sort((a, b) => (a - b));
101+
let indexCount = colors.length;
102+
if (usesTransparency) {
103+
++indexCount;
104+
}
105+
return { colors, usesTransparency, indexCount };
106+
}
65107
}
66108

67109
GifFrame.DisposeToAnything = 0;

test/test_bitmap.js

Lines changed: 0 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -92,71 +92,6 @@ describe("GifFrame bad construction behavior", () => {
9292
});
9393
});
9494

95-
describe("BitmapImage palette", () => {
96-
97-
it("is monocolor without transparency", (done) => {
98-
99-
const bitmap = Tools.getBitmap('singleFrameMonoOpaque');
100-
const f = new BitmapImage(bitmap);
101-
const p = f.getPalette();
102-
assert.deepStrictEqual(p.colors, [0xFF0000]);
103-
assert.strictEqual(p.usesTransparency, false);
104-
done();
105-
});
106-
107-
it("includes two colors without transparency", (done) => {
108-
109-
const bitmap = Tools.getBitmap('singleFrameBWOpaque');
110-
const f = new BitmapImage(bitmap);
111-
const p = f.getPalette();
112-
assert.deepStrictEqual(p.colors, [0x000000, 0xffffff]);
113-
assert.strictEqual(p.usesTransparency, false);
114-
done();
115-
});
116-
117-
it("includes multiple colors without transparency", (done) => {
118-
119-
const bitmap = Tools.getBitmap('singleFrameMultiOpaque');
120-
const f = new BitmapImage(bitmap);
121-
const p = f.getPalette();
122-
assert.deepStrictEqual(p.colors,
123-
[0x0000ff, 0x00ff00, 0xff0000, 0xffffff]);
124-
assert.strictEqual(p.usesTransparency, false);
125-
done();
126-
});
127-
128-
it("has only transparency", (done) => {
129-
130-
const bitmap = Tools.getBitmap('singleFrameNoColorTrans');
131-
const f = new BitmapImage(bitmap);
132-
const p = f.getPalette();
133-
assert.deepStrictEqual(p.colors, []);
134-
assert.strictEqual(p.usesTransparency, true);
135-
done();
136-
});
137-
138-
it("is monocolor with transparency", (done) => {
139-
140-
const bitmap = Tools.getBitmap('singleFrameMonoTrans');
141-
const f = new BitmapImage(bitmap);
142-
const p = f.getPalette();
143-
assert.deepStrictEqual(p.colors, [0x00ff00]);
144-
assert.strictEqual(p.usesTransparency, true);
145-
done();
146-
});
147-
148-
it("includes multiple colors with transparency", (done) => {
149-
150-
const bitmap = Tools.getBitmap('singleFrameMultiPartialTrans');
151-
const f = new BitmapImage(bitmap);
152-
const p = f.getPalette();
153-
assert.deepStrictEqual(p.colors,
154-
[0x000000, 0x0000ff, 0x00ff00, 0xff0000, 0xffffff]);
155-
assert.strictEqual(p.usesTransparency, true);
156-
done();
157-
});
158-
});
159-
16095
// TBD: test BitmapImage transformation methods
16196

16297
describe("Jimp compatibility", () => {

test/test_frame.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,71 @@ describe("GifFrame bad construction behavior", () => {
202202
});
203203
});
204204

205+
describe("GifFrame palette", () => {
206+
207+
it("is monocolor without transparency", (done) => {
208+
209+
const bitmap = Tools.getBitmap('singleFrameMonoOpaque');
210+
const f = new GifFrame(bitmap);
211+
const p = f.getPalette();
212+
assert.deepStrictEqual(p.colors, [0xFF0000]);
213+
assert.strictEqual(p.usesTransparency, false);
214+
done();
215+
});
216+
217+
it("includes two colors without transparency", (done) => {
218+
219+
const bitmap = Tools.getBitmap('singleFrameBWOpaque');
220+
const f = new GifFrame(bitmap);
221+
const p = f.getPalette();
222+
assert.deepStrictEqual(p.colors, [0x000000, 0xffffff]);
223+
assert.strictEqual(p.usesTransparency, false);
224+
done();
225+
});
226+
227+
it("includes multiple colors without transparency", (done) => {
228+
229+
const bitmap = Tools.getBitmap('singleFrameMultiOpaque');
230+
const f = new GifFrame(bitmap);
231+
const p = f.getPalette();
232+
assert.deepStrictEqual(p.colors,
233+
[0x0000ff, 0x00ff00, 0xff0000, 0xffffff]);
234+
assert.strictEqual(p.usesTransparency, false);
235+
done();
236+
});
237+
238+
it("has only transparency", (done) => {
239+
240+
const bitmap = Tools.getBitmap('singleFrameNoColorTrans');
241+
const f = new GifFrame(bitmap);
242+
const p = f.getPalette();
243+
assert.deepStrictEqual(p.colors, []);
244+
assert.strictEqual(p.usesTransparency, true);
245+
done();
246+
});
247+
248+
it("is monocolor with transparency", (done) => {
249+
250+
const bitmap = Tools.getBitmap('singleFrameMonoTrans');
251+
const f = new GifFrame(bitmap);
252+
const p = f.getPalette();
253+
assert.deepStrictEqual(p.colors, [0x00ff00]);
254+
assert.strictEqual(p.usesTransparency, true);
255+
done();
256+
});
257+
258+
it("includes multiple colors with transparency", (done) => {
259+
260+
const bitmap = Tools.getBitmap('singleFrameMultiPartialTrans');
261+
const f = new GifFrame(bitmap);
262+
const p = f.getPalette();
263+
assert.deepStrictEqual(p.colors,
264+
[0x000000, 0x0000ff, 0x00ff00, 0xff0000, 0xffffff]);
265+
assert.strictEqual(p.usesTransparency, true);
266+
done();
267+
});
268+
});
269+
205270
function _assertDefaultFrameOptions(frame) {
206271
assert.strictEqual(frame.xOffset, 0);
207272
assert.strictEqual(frame.yOffset, 0);

test/test_quantize.js

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ describe("graphics color index reduction", () => {
1717
return _graphicsTest('quantizeDekker', 256);
1818
});
1919

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

2322
return _graphicsTest('quantizeSorokin', 32);
@@ -146,6 +145,15 @@ function _graphicsTest(method, maxColors) {
146145
})
147146
}
148147

148+
function _hasTransparency(colorSet) {
149+
for (let rgba of colorSet.values()) {
150+
if ((rgba & 0xff) === 0x00) {
151+
return true;
152+
}
153+
}
154+
return false;
155+
}
156+
149157
function _photoTest(method, maxColors) {
150158
return _reductionTest("hairstreak.jpg", false, method, maxColors);
151159
}
@@ -171,9 +179,9 @@ function _reductionTest(sourceFile, usesTransparency, method, maxColors, modifie
171179
if (err) return reject(err);
172180
work = new BitmapImage(manyJimp.bitmap);
173181

174-
const inputPalette = work.getPalette();
175-
assert.strictEqual(inputPalette.usesTransparency, usesTransparency, label);
176-
assert.isAtLeast(inputPalette.indexCount, maxColors + 1, label);
182+
const inputColorSet = work.getRGBASet();
183+
assert.strictEqual(_hasTransparency(inputColorSet), usesTransparency, label);
184+
assert.isAtLeast(inputColorSet.size, maxColors + 1, label);
177185

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

190-
const outputPalette = work.getPalette();
191-
assert.strictEqual(outputPalette.usesTransparency, usesTransparency, label);
192-
assert.isAtMost(outputPalette.indexCount, maxColors, label);
198+
const outputColorSet = work.getRGBASet();
199+
assert.strictEqual(_hasTransparency(outputColorSet), usesTransparency, label);
200+
assert.isAtMost(outputColorSet.size, maxColors, label);
193201
resolve();
194202
});
195203
});

0 commit comments

Comments
 (0)