public boolean addFrame(Bitmap im) {
        if ((im == null) || !started) {
            return false;
        }
        boolean ok = true;
        try {
            if (!sizeSet) {
                // use first frame's size
                setSize(im.getWidth(), im.getHeight());
            }
            image = im;
            getImagePixels(); // convert to correct format if necessary
            analyzePixels(); // build color table & map pixels
            if (firstFrame) {
                writeLSD(); // logical screen descriptior
                writePalette(); // global color table
                if (repeat >= 0) {
                    // use NS app extension to indicate reps
                    writeNetscapeExt();
                }
            }
            writeGraphicCtrlExt(); // write graphic control extension
            writeImageDesc(); // image descriptor
            if (!firstFrame) {
                writePalette(); // local color table
            }
            writePixels(); // encode and write pixel data
            firstFrame = false;
        } catch (IOException e) {
            ok = false;
        }

        return ok;
    }
Adds next GIF frame. The frame is not written immediately, but is actually
deferred until the next frame is received so that timing data can be
inserted. Invoking <code>finish()</code> flushes all frames. If
<code>setSize</code> was not invoked, the size of the first image is used
for all subsequent frames.

@param im BufferedImage containing frame to write.
@return true if successful.
subs/gif-revert/src/main/java/com/engineer/gif/revert/lib/AnimatedGifEncoder.java