How to read and write VP8L and ALPH chunks of Webp images

335 views Asked by At

I am trying these classes written in pure java code which used for encode the animated WEBP from list of static images. It is based on Google's Webp container specification. I tried to modify the codes to fix some errors. Now

Problem: If image contains VP8L then encoder does not produce valid animated WEBP image (its blank image with 2.5MB size, Supplying 2 images with size of input images). If I set the quality while compressing WEBP to below 100 and then it read chunks as RIFF-WEBP-VP8X-ICCP-ALPH-VP8 in sequence. It creates an animated images with solid black background. It drops the transparent background data maybe, but it animates through each frame.

Code for writing ANMF chunk:

// ANMF chunk
private void writeAnmf(WebpChunk chunk) throws IOException {

   write(new byte[] { 'A', 'N', 'M', 'F' });
   writeUInt32(chunk.payload.length + 24);

   writeUInt24(chunk.x); // 3 bytes (3)
   writeUInt24(chunk.y); // 3 bytes (6)
   writeUInt24(chunk.width); // 3 bytes (9)
   writeUInt24(chunk.height); // 3 bytes (12)
   writeUInt24(chunk.duration); // 3 bytes (15)

   BitSet bs = new BitSet(6);
   bs.set(1, chunk.useAlphaBlending);
   bs.set(0, chunk.disposeToBackgroundColor);
   write(bitSetToBytes(bs, 1)); // 1 byte (16)


   if (chunk.isLossless) {
      write(new byte[] { 'V', 'P', '8', 'L' }); // 4 bytes (20)
      Log.d(Tag,"writting vp8l ; isLossLess");
   } else {
      Log.d(Tag,"writting vp8 ; Lossy");
      write(new byte[] { 'V', 'P', '8', ' ' });
   }
   writeUInt32(chunk.payload.length-16h); // 4 bytes (24)
   write(chunk.payload);

}
// ANIM chunk
private WebpChunk readVp8x() throws IOException {
        int chunkSize = readUInt32();
        if (chunkSize != 10)
            throw new IOException("Expected 10 bytes for VP8X.");

        WebpChunk chunk = new WebpChunk(WebpChunkType.VP8X);

        byte[] flags = new byte[4];
        read(flags, 4);
        BitSet bs = BitSet.valueOf(flags);

        chunk.hasIccp = bs.get(0);
        chunk.hasAnim = bs.get(1);
        chunk.hasExif = bs.get(2);
        chunk.hasXmp = bs.get(3);
        chunk.hasAlpha = bs.get(4);

        chunk.width = readUInt24();
        chunk.height = readUInt24();

        debug(String.format("VP8X: size = %dx%d", chunk.width, chunk.height));
        return chunk;
    }

Code for reading VP8L chunk:

    private WebpChunk readVp8l() throws IOException {
        Log.d(Tag,"readVp8l() body ");
        int chunkSize = readUInt32();

        WebpChunk chunk = new WebpChunk(WebpChunkType.VP8L);
        chunk.isLossless = true;
        chunk.payload = readPayload(chunkSize);

        debug(String.format("VP8L: bytes = %d", chunkSize));
        return chunk;
    }

function for writing bytes to output stream

    private void write(byte[] bytes, int length) throws IOException {
        _outputStream.write(bytes, length);
        _offset += length;
    }

It read chunks as RIFF[size]WEBP -> VP8X - ICCP - ALPH - VP8L and writing as RIFF[size]WEBP -> VP8X - ANIM - ANMF (for first frame) and then ANMF for rest of frames. what is going wrong with these codes?

1

There are 1 answers

0
CrackerKSR On BEST ANSWER

Found out the solution by examining more. reading ALPH: We need to store the alpha data (Alpha BitStream) somewhere so that we can use that while writing the frames. writing ANMF: while writing ANMF, after writing ANMF metadata we need to write the ALPH header > BitStream length > BitStream respectively. Also we must add this length to ANMF chunk size. The ALPH chunk must be written before VP8/VP8L chunk.

private void writeAnmf(WebpChunk chunk) throws IOException {
        write(new byte[] { 'A', 'N', 'M', 'F' });

        int alphSize = 0;
        if(chunk.alphaData != null){
            alphSize = 8+chunk.alphaData.length;
        }

        writeUInt32(chunk.payload.length + 24 + alphSize);

        writeUInt24(chunk.x); // 3 bytes (3)
        writeUInt24(chunk.y); // 3 bytes (6)
        writeUInt24(chunk.width); // 3 bytes (9)
        writeUInt24(chunk.height); // 3 bytes (12)
        writeUInt24(chunk.duration); // 3 bytes (15)

        BitSet bs = new BitSet(8);
        bs.set(0, true);
        bs.set(1, chunk.disposeToBackgroundColor);
        write(bitSetToBytes(bs, 1)); // 1 byte (16)

        if (chunk.alphaData != null){
            write(new byte[] { 'A', 'L', 'P', 'H' }); // 4
            writeUInt32(chunk.alphaData.length); // 4
            write(chunk.alphaData);
            Log.e("WebpDemo","ALPH ");
        }


        if (chunk.isLossless)
            write(new byte[] { 'V', 'P', '8', 'L' }); // 4 bytes (20)
        else
            write(new byte[] { 'V', 'P', '8', ' ' });

        writeUInt32(chunk.payload.length); // 4 bytes (24)
        write(chunk.payload);


    }