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?
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);
}