|
1 | | -const {GIFEncoder} = require('react-native-gifencoder'); |
| 1 | +import {GIFEncoder, quantize, applyPalette} from 'gifenc'; |
2 | 2 | const {Buffer} = require('buffer'); |
3 | 3 |
|
4 | | -// ref to https://github.com/flyskywhy/PixelShapeRN/blob/v1.1.27/src/workers/generateGif.worker.js#L22 |
5 | | -const isTransparencyPresent = (imageDataArr, transparentColor) => { |
6 | | - let i = 0, |
7 | | - transpUsed = -1; |
8 | | - const len = imageDataArr.length; |
9 | | - |
10 | | - for (; i < len; ) { |
11 | | - transpUsed *= |
12 | | - imageDataArr[i++] - |
13 | | - transparentColor.r + |
14 | | - imageDataArr[i++] - |
15 | | - transparentColor.g + |
16 | | - imageDataArr[i++] - |
17 | | - transparentColor.b; |
18 | | - |
19 | | - i++; |
20 | | - |
21 | | - if (!transpUsed) { |
22 | | - break; |
23 | | - } |
24 | | - transpUsed = -1; |
25 | | - } |
26 | | - return !transpUsed; |
27 | | -}; |
28 | | - |
29 | 4 | class Encoder { |
30 | 5 | constructor(config) { |
31 | 6 | this.config = config; |
32 | 7 | } |
33 | 8 |
|
34 | 9 | encodeGif(imageDatas, width, height) { |
35 | | - const gif = new GIFEncoder(); |
36 | | - const transparentColor = 0x000000; |
37 | | - const transpRGB = {r: 0, g: 0, b: 0}; |
| 10 | + // Create an encoding stream |
| 11 | + const gif = GIFEncoder(); |
| 12 | + |
| 13 | + const delay = this.config.delayPerFrame; |
38 | 14 |
|
39 | | - gif.setRepeat(0); |
40 | | - // we need to set disposal code of 2 for each frame |
41 | | - // to be sure that the current frame will override the previous and won't overlap |
42 | | - gif.setDispose(2); |
43 | | - gif.setQuality(this.config.quality); |
44 | | - gif.setDelay(this.config.delayPerFrame); |
45 | | - gif.setSize(width, height); |
46 | | - gif.setComment(''); |
| 15 | + const framesLength = imageDatas.length; |
| 16 | + for (let i = 0; i < framesLength; i++) { |
| 17 | + const data = imageDatas[i].data; |
47 | 18 |
|
48 | | - let frames = []; |
49 | | - imageDatas.map((imageData, index) => { |
50 | | - const useTransparency = isTransparencyPresent(imageData.data, transpRGB); |
51 | | - if (useTransparency) { |
52 | | - gif.setTransparent(transparentColor); |
53 | | - } else { |
54 | | - gif.setTransparent(null); |
55 | | - } |
| 19 | + // Quantize your colors to a 256-color RGB palette palette |
| 20 | + const palette = quantize(data, 256); |
56 | 21 |
|
57 | | - if (index === 0) { |
58 | | - gif.start(); |
59 | | - } else { |
60 | | - gif.cont(); |
61 | | - gif.setProperties(true, false); // started, firstFrame |
62 | | - } |
| 22 | + // Get an indexed bitmap by reducing each pixel to the nearest color palette |
| 23 | + const index = applyPalette(data, palette); |
63 | 24 |
|
64 | | - gif.addFrame(imageData.data, true); |
| 25 | + // Write a single frame |
| 26 | + gif.writeFrame(index, width, height, { |
| 27 | + palette, |
| 28 | + delay, |
| 29 | + transparent: true, |
| 30 | + // dispose: 2, |
| 31 | + }); |
| 32 | + |
| 33 | + // Wait a tick so that we don't lock up browser |
| 34 | + // await new Promise(resolve => setTimeout(resolve, 0)); |
| 35 | + } |
65 | 36 |
|
66 | | - if (imageDatas.length === index + 1) { |
67 | | - gif.finish(); |
68 | | - } |
| 37 | + // Write end-of-stream character |
| 38 | + gif.finish(); |
69 | 39 |
|
70 | | - frames = frames.concat(gif.stream().bin); |
71 | | - }); |
| 40 | + // Get the Uint8Array output of your binary GIF file |
| 41 | + const output = gif.bytes(); |
72 | 42 |
|
73 | 43 | if (this.config.returnBufferType === 'Buffer') { |
74 | | - return Buffer.from(frames); |
| 44 | + return Buffer.from(output); |
75 | 45 | } else { |
76 | | - return frames; |
| 46 | + return Array.from(output); |
77 | 47 | } |
78 | 48 | } |
79 | 49 |
|
|
0 commit comments