Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for alpha channel and bug fix #5

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public class WebpChunk {
public boolean useAlphaBlending;
public boolean disposeToBackgroundColor;

public byte[] alphaData;

public WebpChunk(WebpChunkType t) {
type = t;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@ public enum WebpChunkType {
VP8,
VP8L,
ANIM,
ANMF
ANMF,
ICCP,
ALPH
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.n4no.webpencoder.webp.muxer;

import android.util.Log;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
Expand Down Expand Up @@ -52,6 +55,10 @@ public WebpChunk read() throws IOException {
return readAnim();
if (isFourCc(fcc, 'A', 'N', 'M', 'F'))
return readAnmf();
if (isFourCc(fcc, 'I', 'C', 'C', 'P'))
return readIccp();
if (isFourCc(fcc, 'A', 'L', 'P', 'H'))
return readAlph();

throw new IOException(String.format("Not supported FourCC: %c.%c.%c.%c.",
fcc[0], fcc[1], fcc[2], fcc[3]));
Expand All @@ -63,6 +70,24 @@ public WebpChunk read() throws IOException {
return null;
}

private WebpChunk readAlph() throws IOException {
int chunkSize = readUInt32();
WebpChunk chunk = new WebpChunk(WebpChunkType.ALPH);
chunk.alphaData = readPayload(chunkSize);
chunk.hasAlpha = true;
return chunk;
}

private WebpChunk readIccp() throws IOException {
int chunkSize = readUInt32();
WebpChunk chunk = new WebpChunk(WebpChunkType.ICCP);

readPayload(chunkSize);
// no need to store the payload to this chunk as Animated Webp does not require ICCP

return chunk;
}

private WebpChunk readVp8x() throws IOException {
int chunkSize = readUInt32();
if (chunkSize != 10)
Expand All @@ -74,11 +99,12 @@ private WebpChunk readVp8x() throws IOException {
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);
bs.get(0); // R reserved
chunk.hasAnim = bs.get(1); // A Animation
chunk.hasXmp = bs.get(2); // X XMP
chunk.hasExif = bs.get(3); // E Exif
chunk.hasAlpha = bs.get(4); // L Alpha
chunk.hasIccp = bs.get(5); // I ICCP

chunk.width = readUInt24();
chunk.height = readUInt24();
Expand Down Expand Up @@ -110,7 +136,10 @@ private WebpChunk readVp8l() throws IOException {

WebpChunk chunk = new WebpChunk(WebpChunkType.VP8L);
chunk.isLossless = true;
chunk.payload = readPayload(chunkSize);
// chunkSize is not telling the correct size of payload to read.
// chunk.payload = readPayload(chunkSize);
// So reading all bytes remained (Max 1024 bytes)
chunk.payload = readAllBytes();

debug(String.format("VP8L: bytes = %d", chunkSize));
return chunk;
Expand Down Expand Up @@ -193,6 +222,17 @@ private boolean isFourCc(byte[] h, char a, char b, char c, char d) {
return h[0] == a && h[1] == b && h[2] == c && h[3] == d;
}

private byte[] readAllBytes() throws IOException {
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int numRead;
while ((numRead = _inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, numRead);
_offset += numRead;
}
return outputStream.toByteArray();
}
}
private void debug(String message) {
if (_debug)
System.out.println(message);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.n4no.webpencoder.webp.muxer;

import android.util.Log;

import com.n4no.webpencoder.webp.muxer.stream.SeekableOutputStream;

import java.io.IOException;
Expand Down Expand Up @@ -87,7 +89,15 @@ private void writeAnim(WebpChunk chunk) throws IOException {

private void writeAnmf(WebpChunk chunk) throws IOException {
write(new byte[] { 'A', 'N', 'M', 'F' });
writeUInt32(chunk.payload.length + 24);

// if ALPH chunk present, get size
int AlphaSize = 0;
if(chunk.alphaData != null){
AlphaSize = 4 + 4 + chunk.alphaData.length;
// 4 bytes (ALPH header) + 4 bytes (chunk size) + length of Alpha BitStream
}

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

writeUInt24(chunk.x); // 3 bytes (3)
writeUInt24(chunk.y); // 3 bytes (6)
Expand All @@ -100,6 +110,11 @@ private void writeAnmf(WebpChunk chunk) throws IOException {
bs.set(0, chunk.disposeToBackgroundColor);
write(bitSetToBytes(bs, 1)); // 1 byte (16)

// Insert ALPH chunk
if(chunk.alphaData != null){
writeAlph(chunk.alphaData);
}

if (chunk.isLossless)
write(new byte[] { 'V', 'P', '8', 'L' }); // 4 bytes (20)
else
Expand All @@ -108,6 +123,12 @@ private void writeAnmf(WebpChunk chunk) throws IOException {
write(chunk.payload);
}

private void writeAlph(byte[] alphaData) throws IOException {
write(new byte[] { 'A', 'L', 'P', 'H' }); // 4
writeUInt32(alphaData.length); // 4
write(alphaData); // x
}

//

private void write(byte[] bytes) throws IOException {
Expand Down
24 changes: 19 additions & 5 deletions app/src/main/java/com/n4no/webpencoder/webp/muxer/WebpMuxer.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.n4no.webpencoder.webp.muxer;

import android.util.Log;

import java.io.IOException;
import java.io.InputStream;

Expand All @@ -16,6 +18,9 @@ public class WebpMuxer {
private int _width;
private int _height;

private byte[] _alphaData;
private boolean _hasAlpha;

public WebpMuxer(WebpContainerWriter writer) {
_writer = writer;
}
Expand All @@ -42,13 +47,13 @@ public void writeFirstFrameFromWebm(InputStream inputStream) throws IOException
WebpChunk chunk = readFirstChunkWithPayload(reader);
reader.close();

writeFrame(chunk.payload, chunk.isLossless);
writeFrame(chunk, chunk.payload, chunk.isLossless);
}

public void writeFrame(byte[] payload, boolean isLossless) throws IOException {
public void writeFrame(WebpChunk chunk,byte[] payload, boolean isLossless) throws IOException {
if (_isFirstFrame) {
_isFirstFrame = false;
writeHeader();
writeHeader(chunk);
}

if (hasAnim())
Expand All @@ -70,18 +75,25 @@ private boolean hasAnim() {
private WebpChunk readFirstChunkWithPayload(WebpContainerReader reader) throws IOException {
WebpChunk chunk;
while ((chunk = reader.read()) != null) {

if (chunk.type == WebpChunkType.ALPH)
_alphaData = chunk.alphaData;

if (chunk.type == WebpChunkType.VP8X)
_hasAlpha = chunk.hasAlpha;

if (chunk.payload != null)
return chunk;
}
throw new IOException("Can not find chunk with payload.");
}

private void writeHeader() throws IOException {
private void writeHeader(WebpChunk chunk) throws IOException {
_writer.writeHeader();

WebpChunk vp8x = new WebpChunk(WebpChunkType.VP8X);
vp8x.hasAnim = hasAnim();
vp8x.hasAlpha = false;
vp8x.hasAlpha = _hasAlpha;
vp8x.hasXmp = false;
vp8x.hasExif = false;
vp8x.hasIccp = false;
Expand Down Expand Up @@ -111,6 +123,8 @@ private void writeAnmf(byte[] payload, boolean isLossless) throws IOException {
anmf.useAlphaBlending = false;
anmf.disposeToBackgroundColor = false;

anmf.alphaData = _alphaData;

_writer.write(anmf);
}

Expand Down