www in docker support
This commit is contained in:
parent
539a848e95
commit
c227fce036
2145 changed files with 399596 additions and 58 deletions
|
|
@ -0,0 +1,68 @@
|
|||
package org.jcodec.codecs.png;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import org.jcodec.common.model.ColorSpace;
|
||||
|
||||
class IHDR {
|
||||
static final int PNG_COLOR_MASK_ALPHA = 4;
|
||||
|
||||
static final int PNG_COLOR_MASK_COLOR = 2;
|
||||
|
||||
static final int PNG_COLOR_MASK_PALETTE = 1;
|
||||
|
||||
int width;
|
||||
|
||||
int height;
|
||||
|
||||
byte bitDepth;
|
||||
|
||||
byte colorType;
|
||||
|
||||
private byte compressionType;
|
||||
|
||||
private byte filterType;
|
||||
|
||||
byte interlaceType;
|
||||
|
||||
void write(ByteBuffer data) {
|
||||
data.putInt(this.width);
|
||||
data.putInt(this.height);
|
||||
data.put(this.bitDepth);
|
||||
data.put(this.colorType);
|
||||
data.put(this.compressionType);
|
||||
data.put(this.filterType);
|
||||
data.put(this.interlaceType);
|
||||
}
|
||||
|
||||
void parse(ByteBuffer data) {
|
||||
this.width = data.getInt();
|
||||
this.height = data.getInt();
|
||||
this.bitDepth = data.get();
|
||||
this.colorType = data.get();
|
||||
this.compressionType = data.get();
|
||||
this.filterType = data.get();
|
||||
this.interlaceType = data.get();
|
||||
data.getInt();
|
||||
}
|
||||
|
||||
int rowSize() {
|
||||
return this.width * getBitsPerPixel() + 7 >> 3;
|
||||
}
|
||||
|
||||
private int getNBChannels() {
|
||||
int channels = 1;
|
||||
if ((this.colorType & 0x3) == 2)
|
||||
channels = 3;
|
||||
if ((this.colorType & 0x4) != 0)
|
||||
channels++;
|
||||
return channels;
|
||||
}
|
||||
|
||||
int getBitsPerPixel() {
|
||||
return this.bitDepth * getNBChannels();
|
||||
}
|
||||
|
||||
ColorSpace colorSpace() {
|
||||
return ColorSpace.RGB;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package org.jcodec.codecs.png;
|
||||
|
||||
public class PNGConsts {
|
||||
static final long PNGSIG = -8552249625308161526L;
|
||||
|
||||
static final int PNGSIGhi = -1991225785;
|
||||
|
||||
static final int MNGSIGhi = -1974645177;
|
||||
|
||||
static final int PNGSIGlo = 218765834;
|
||||
|
||||
static final int MNGSIGlo = 218765834;
|
||||
|
||||
static final int TAG_IHDR = 1229472850;
|
||||
|
||||
static final int TAG_IDAT = 1229209940;
|
||||
|
||||
static final int TAG_PLTE = 1347179589;
|
||||
|
||||
static final int TAG_tRNS = 1951551059;
|
||||
|
||||
static final int TAG_IEND = 1229278788;
|
||||
}
|
||||
|
|
@ -0,0 +1,461 @@
|
|||
package org.jcodec.codecs.png;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.zip.DataFormatException;
|
||||
import java.util.zip.Inflater;
|
||||
import org.jcodec.common.VideoCodecMeta;
|
||||
import org.jcodec.common.VideoDecoder;
|
||||
import org.jcodec.common.io.NIOUtils;
|
||||
import org.jcodec.common.logging.Logger;
|
||||
import org.jcodec.common.model.ColorSpace;
|
||||
import org.jcodec.common.model.Picture;
|
||||
import org.jcodec.common.model.Size;
|
||||
import org.jcodec.common.tools.MathUtil;
|
||||
|
||||
public class PNGDecoder extends VideoDecoder {
|
||||
private static final int FILTER_TYPE_LOCO = 64;
|
||||
|
||||
private static final int FILTER_VALUE_NONE = 0;
|
||||
|
||||
private static final int FILTER_VALUE_SUB = 1;
|
||||
|
||||
private static final int FILTER_VALUE_UP = 2;
|
||||
|
||||
private static final int FILTER_VALUE_AVG = 3;
|
||||
|
||||
private static final int FILTER_VALUE_PAETH = 4;
|
||||
|
||||
private static final int PNG_COLOR_TYPE_GRAY = 0;
|
||||
|
||||
private static final int PNG_COLOR_TYPE_PALETTE = 3;
|
||||
|
||||
private static final int PNG_COLOR_TYPE_RGB = 2;
|
||||
|
||||
private static final int alphaR = 127;
|
||||
|
||||
private static final int alphaG = 127;
|
||||
|
||||
private static final int alphaB = 127;
|
||||
|
||||
private static final int[] logPassStep = new int[] { 3, 3, 2, 2, 1, 1, 0 };
|
||||
|
||||
private static final int[] logPassRowStep = new int[] { 3, 3, 3, 2, 2, 1, 1 };
|
||||
|
||||
private static final int[] passOff = new int[] { 0, 4, 0, 2, 0, 1, 0 };
|
||||
|
||||
private static final int[] passRowOff = new int[] { 0, 0, 4, 0, 2, 0, 1 };
|
||||
|
||||
private byte[] ca = new byte[4];
|
||||
|
||||
public Picture decodeFrame(ByteBuffer data, byte[][] buffer) {
|
||||
if (!ispng(data))
|
||||
throw new RuntimeException("Not a PNG file.");
|
||||
IHDR ihdr = null;
|
||||
PLTE plte = null;
|
||||
TRNS trns = null;
|
||||
List<ByteBuffer> list = new ArrayList<>();
|
||||
while (data.remaining() >= 8) {
|
||||
int length = data.getInt();
|
||||
int tag = data.getInt();
|
||||
if (data.remaining() < length)
|
||||
break;
|
||||
switch (tag) {
|
||||
case 1229472850:
|
||||
ihdr = new IHDR();
|
||||
ihdr.parse(data);
|
||||
continue;
|
||||
case 1347179589:
|
||||
plte = new PLTE();
|
||||
plte.parse(data, length);
|
||||
continue;
|
||||
case 1951551059:
|
||||
if (ihdr == null)
|
||||
throw new IllegalStateException("tRNS tag before IHDR");
|
||||
trns = new TRNS(ihdr.colorType);
|
||||
trns.parse(data, length);
|
||||
continue;
|
||||
case 1229209940:
|
||||
list.add(NIOUtils.read(data, length));
|
||||
NIOUtils.skip(data, 4);
|
||||
continue;
|
||||
case 1229278788:
|
||||
NIOUtils.skip(data, 4);
|
||||
continue;
|
||||
}
|
||||
data.position(data.position() + length + 4);
|
||||
}
|
||||
if (ihdr != null) {
|
||||
try {
|
||||
decodeData(ihdr, plte, trns, list, buffer);
|
||||
} catch (DataFormatException e) {
|
||||
return null;
|
||||
}
|
||||
return Picture.createPicture(ihdr.width, ihdr.height, buffer, ihdr.colorSpace());
|
||||
}
|
||||
throw new IllegalStateException("no IHDR tag");
|
||||
}
|
||||
|
||||
private void decodeData(IHDR ihdr, PLTE plte, TRNS trns, List<ByteBuffer> list, byte[][] buffer) throws DataFormatException {
|
||||
int bpp = ihdr.getBitsPerPixel() + 7 >> 3;
|
||||
int passes = (ihdr.interlaceType == 0) ? 1 : 7;
|
||||
Inflater inflater = new Inflater();
|
||||
Iterator<ByteBuffer> it = list.iterator();
|
||||
for (int pass = 0; pass < passes; pass++) {
|
||||
int rowSize, rowStart, rowStep, colStart, colStep;
|
||||
if (ihdr.interlaceType == 0) {
|
||||
rowSize = ihdr.rowSize() + 1;
|
||||
colStart = rowStart = 0;
|
||||
colStep = rowStep = 1;
|
||||
} else {
|
||||
int round = (1 << logPassStep[pass]) - 1;
|
||||
rowSize = (ihdr.width + round >> logPassStep[pass]) + 1;
|
||||
rowStart = passRowOff[pass];
|
||||
rowStep = 1 << logPassRowStep[pass];
|
||||
colStart = passOff[pass];
|
||||
colStep = 1 << logPassStep[pass];
|
||||
}
|
||||
byte[] lastRow = new byte[rowSize - 1];
|
||||
byte[] uncompressed = new byte[rowSize];
|
||||
int bptr = 3 * (ihdr.width * rowStart + colStart);
|
||||
for (int row = rowStart; row < ihdr.height; row += rowStep) {
|
||||
int count = inflater.inflate(uncompressed);
|
||||
if (count < uncompressed.length && inflater.needsInput()) {
|
||||
if (!it.hasNext()) {
|
||||
Logger.warn(String.format("Data truncation at row %d", row));
|
||||
break;
|
||||
}
|
||||
ByteBuffer next = it.next();
|
||||
inflater.setInput(NIOUtils.toArray(next));
|
||||
int toRead = uncompressed.length - count;
|
||||
count = inflater.inflate(uncompressed, count, toRead);
|
||||
if (count != toRead) {
|
||||
Logger.warn(String.format("Data truncation at row %d", row));
|
||||
break;
|
||||
}
|
||||
}
|
||||
int filter = uncompressed[0];
|
||||
switch (filter) {
|
||||
case 0:
|
||||
System.arraycopy(uncompressed, 1, lastRow, 0, rowSize - 1);
|
||||
break;
|
||||
case 1:
|
||||
filterSub(uncompressed, rowSize - 1, lastRow, bpp);
|
||||
break;
|
||||
case 2:
|
||||
filterUp(uncompressed, rowSize - 1, lastRow);
|
||||
break;
|
||||
case 3:
|
||||
filterAvg(uncompressed, rowSize - 1, lastRow, bpp);
|
||||
break;
|
||||
case 4:
|
||||
filterPaeth(uncompressed, rowSize - 1, lastRow, bpp);
|
||||
break;
|
||||
}
|
||||
int bptrWas = bptr;
|
||||
if ((ihdr.colorType & 0x1) != 0) {
|
||||
for (int i = 0; i < rowSize - 1; i += bpp, bptr += 3 * colStep) {
|
||||
int plt = plte.palette[lastRow[i] & 0xFF];
|
||||
buffer[0][bptr] = (byte)((plt >> 16 & 0xFF) - 128);
|
||||
buffer[0][bptr + 1] = (byte)((plt >> 8 & 0xFF) - 128);
|
||||
buffer[0][bptr + 2] = (byte)((plt & 0xFF) - 128);
|
||||
}
|
||||
} else if ((ihdr.colorType & 0x2) != 0) {
|
||||
for (int i = 0; i < rowSize - 1; i += bpp, bptr += 3 * colStep) {
|
||||
buffer[0][bptr] = (byte)((lastRow[i] & 0xFF) - 128);
|
||||
buffer[0][bptr + 1] = (byte)((lastRow[i + 1] & 0xFF) - 128);
|
||||
buffer[0][bptr + 2] = (byte)((lastRow[i + 2] & 0xFF) - 128);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < rowSize - 1; i += bpp, bptr += 3 * colStep) {
|
||||
buffer[0][bptr + 2] = (byte)((lastRow[i] & 0xFF) - 128);
|
||||
buffer[0][bptr + 1] = (byte)((lastRow[i] & 0xFF) - 128);
|
||||
buffer[0][bptr] = (byte)((lastRow[i] & 0xFF) - 128);
|
||||
}
|
||||
}
|
||||
if ((ihdr.colorType & 0x4) != 0) {
|
||||
for (int i = bpp - 1, j = bptrWas; i < rowSize - 1; i += bpp, j += 3 * colStep) {
|
||||
int alpha = lastRow[i] & 0xFF, nalpha = 256 - alpha;
|
||||
buffer[0][j] = (byte)(127 * nalpha + buffer[0][j] * alpha >> 8);
|
||||
buffer[0][j + 1] = (byte)(127 * nalpha + buffer[0][j + 1] * alpha >> 8);
|
||||
buffer[0][j + 2] = (byte)(127 * nalpha + buffer[0][j + 2] * alpha >> 8);
|
||||
}
|
||||
} else if (trns != null) {
|
||||
if (ihdr.colorType == 3) {
|
||||
for (int i = 0, j = bptrWas; i < rowSize - 1; i++, j += 3 * colStep) {
|
||||
int alpha = trns.alphaPal[lastRow[i] & 0xFF] & 0xFF, nalpha = 256 - alpha;
|
||||
buffer[0][j] = (byte)(127 * nalpha + buffer[0][j] * alpha >> 8);
|
||||
buffer[0][j + 1] = (byte)(127 * nalpha + buffer[0][j + 1] * alpha >> 8);
|
||||
buffer[0][j + 2] = (byte)(127 * nalpha + buffer[0][j + 2] * alpha >> 8);
|
||||
}
|
||||
} else if (ihdr.colorType == 2) {
|
||||
int ar = (trns.alphaR & 0xFF) - 128;
|
||||
int ag = (trns.alphaG & 0xFF) - 128;
|
||||
int ab = (trns.alphaB & 0xFF) - 128;
|
||||
if (ab != 127 || ag != 127 || ar != 127)
|
||||
for (int i = 0, j = bptrWas; i < rowSize - 1; i += bpp, j += 3 * colStep) {
|
||||
if (buffer[0][j] == ar && buffer[0][j + 1] == ag && buffer[0][j + 2] == ab) {
|
||||
buffer[0][j] = Byte.MAX_VALUE;
|
||||
buffer[0][j + 1] = Byte.MAX_VALUE;
|
||||
buffer[0][j + 2] = Byte.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
} else if (ihdr.colorType == 0) {
|
||||
for (int i = 0, j = bptrWas; i < rowSize - 1; i++, j += 3 * colStep) {
|
||||
if (lastRow[i] == trns.alphaGrey) {
|
||||
buffer[0][j] = Byte.MAX_VALUE;
|
||||
buffer[0][j + 1] = Byte.MAX_VALUE;
|
||||
buffer[0][j + 2] = Byte.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bptr = bptrWas + 3 * ihdr.width * rowStep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void filterPaeth(byte[] uncompressed, int rowSize, byte[] lastRow, int bpp) {
|
||||
for (int j = 0; j < bpp; j++) {
|
||||
this.ca[j] = lastRow[j];
|
||||
lastRow[j] = (byte)((uncompressed[j + 1] & 0xFF) + (lastRow[j] & 0xFF));
|
||||
}
|
||||
for (int i = bpp; i < rowSize; i++) {
|
||||
int a = lastRow[i - bpp] & 0xFF;
|
||||
int b = lastRow[i] & 0xFF;
|
||||
int c = this.ca[i % bpp] & 0xFF;
|
||||
int p = b - c;
|
||||
int pc = a - c;
|
||||
int pa = MathUtil.abs(p);
|
||||
int pb = MathUtil.abs(pc);
|
||||
pc = MathUtil.abs(p + pc);
|
||||
if (pa <= pb && pa <= pc) {
|
||||
p = a;
|
||||
} else if (pb <= pc) {
|
||||
p = b;
|
||||
} else {
|
||||
p = c;
|
||||
}
|
||||
this.ca[i % bpp] = lastRow[i];
|
||||
lastRow[i] = (byte)(p + (uncompressed[i + 1] & 0xFF));
|
||||
}
|
||||
}
|
||||
|
||||
private static void filterSub(byte[] uncompressed, int rowSize, byte[] lastRow, int bpp) {
|
||||
switch (bpp) {
|
||||
case 1:
|
||||
filterSub1(uncompressed, lastRow, rowSize);
|
||||
break;
|
||||
case 2:
|
||||
filterSub2(uncompressed, lastRow, rowSize);
|
||||
break;
|
||||
case 3:
|
||||
filterSub3(uncompressed, lastRow, rowSize);
|
||||
break;
|
||||
default:
|
||||
filterSub4(uncompressed, lastRow, rowSize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void filterAvg(byte[] uncompressed, int rowSize, byte[] lastRow, int bpp) {
|
||||
switch (bpp) {
|
||||
case 1:
|
||||
filterAvg1(uncompressed, lastRow, rowSize);
|
||||
break;
|
||||
case 2:
|
||||
filterAvg2(uncompressed, lastRow, rowSize);
|
||||
break;
|
||||
case 3:
|
||||
filterAvg3(uncompressed, lastRow, rowSize);
|
||||
break;
|
||||
default:
|
||||
filterAvg4(uncompressed, lastRow, rowSize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void filterSub1(byte[] uncompressed, byte[] lastRow, int rowSize) {
|
||||
byte p = lastRow[0] = uncompressed[1];
|
||||
for (int i = 1; i < rowSize; i++)
|
||||
p = lastRow[i] = (byte)((p & 0xFF) + (uncompressed[i + 1] & 0xFF));
|
||||
}
|
||||
|
||||
private static void filterUp(byte[] uncompressed, int rowSize, byte[] lastRow) {
|
||||
for (int i = 0; i < rowSize; i++)
|
||||
lastRow[i] = (byte)((lastRow[i] & 0xFF) + (uncompressed[i + 1] & 0xFF));
|
||||
}
|
||||
|
||||
private static void filterAvg1(byte[] uncompressed, byte[] lastRow, int rowSize) {
|
||||
byte p = lastRow[0] = (byte)((uncompressed[1] & 0xFF) + ((lastRow[0] & 0xFF) >> 1));
|
||||
for (int i = 1; i < rowSize; i++)
|
||||
p = lastRow[i] = (byte)(((lastRow[i] & 0xFF) + (p & 0xFF) >> 1) + (uncompressed[i + 1] & 0xFF));
|
||||
}
|
||||
|
||||
private static void filterSub2(byte[] uncompressed, byte[] lastRow, int rowSize) {
|
||||
byte p0 = lastRow[0] = uncompressed[1];
|
||||
byte p1 = lastRow[1] = uncompressed[2];
|
||||
for (int i = 2; i < rowSize; i += 2) {
|
||||
p0 = lastRow[i] = (byte)((p0 & 0xFF) + (uncompressed[1 + i] & 0xFF));
|
||||
p1 = lastRow[i + 1] = (byte)((p1 & 0xFF) + (uncompressed[2 + i] & 0xFF));
|
||||
}
|
||||
}
|
||||
|
||||
private static void filterAvg2(byte[] uncompressed, byte[] lastRow, int rowSize) {
|
||||
byte p0 = lastRow[0] = (byte)((uncompressed[1] & 0xFF) + ((lastRow[0] & 0xFF) >> 1));
|
||||
byte p1 = lastRow[1] = (byte)((uncompressed[2] & 0xFF) + ((lastRow[1] & 0xFF) >> 1));
|
||||
for (int i = 2; i < rowSize; i += 2) {
|
||||
p0 = lastRow[i] = (byte)(((lastRow[i] & 0xFF) + (p0 & 0xFF) >> 1) + (uncompressed[1 + i] & 0xFF));
|
||||
p1 = lastRow[i + 1] = (byte)(((lastRow[i + 1] & 0xFF) + (p1 & 0xFF) >> 1) + (uncompressed[i + 2] & 0xFF));
|
||||
}
|
||||
}
|
||||
|
||||
private static void filterSub3(byte[] uncompressed, byte[] lastRow, int rowSize) {
|
||||
byte p0 = lastRow[0] = uncompressed[1];
|
||||
byte p1 = lastRow[1] = uncompressed[2];
|
||||
byte p2 = lastRow[2] = uncompressed[3];
|
||||
for (int i = 3; i < rowSize; i += 3) {
|
||||
p0 = lastRow[i] = (byte)((p0 & 0xFF) + (uncompressed[i + 1] & 0xFF));
|
||||
p1 = lastRow[i + 1] = (byte)((p1 & 0xFF) + (uncompressed[i + 2] & 0xFF));
|
||||
p2 = lastRow[i + 2] = (byte)((p2 & 0xFF) + (uncompressed[i + 3] & 0xFF));
|
||||
}
|
||||
}
|
||||
|
||||
private static void filterAvg3(byte[] uncompressed, byte[] lastRow, int rowSize) {
|
||||
byte p0 = lastRow[0] = (byte)((uncompressed[1] & 0xFF) + ((lastRow[0] & 0xFF) >> 1));
|
||||
byte p1 = lastRow[1] = (byte)((uncompressed[2] & 0xFF) + ((lastRow[1] & 0xFF) >> 1));
|
||||
byte p2 = lastRow[2] = (byte)((uncompressed[3] & 0xFF) + ((lastRow[2] & 0xFF) >> 1));
|
||||
for (int i = 3; i < rowSize; i += 3) {
|
||||
p0 = lastRow[i] = (byte)(((lastRow[i] & 0xFF) + (p0 & 0xFF) >> 1) + (uncompressed[i + 1] & 0xFF));
|
||||
p1 = lastRow[i + 1] = (byte)(((lastRow[i + 1] & 0xFF) + (p1 & 0xFF) >> 1) + (uncompressed[i + 2] & 0xFF));
|
||||
p2 = lastRow[i + 2] = (byte)(((lastRow[i + 2] & 0xFF) + (p2 & 0xFF) >> 1) + (uncompressed[i + 3] & 0xFF));
|
||||
}
|
||||
}
|
||||
|
||||
private static void filterSub4(byte[] uncompressed, byte[] lastRow, int rowSize) {
|
||||
byte p0 = lastRow[0] = uncompressed[1];
|
||||
byte p1 = lastRow[1] = uncompressed[2];
|
||||
byte p2 = lastRow[2] = uncompressed[3];
|
||||
byte p3 = lastRow[3] = uncompressed[4];
|
||||
for (int i = 4; i < rowSize; i += 4) {
|
||||
p0 = lastRow[i] = (byte)((p0 & 0xFF) + (uncompressed[i + 1] & 0xFF));
|
||||
p1 = lastRow[i + 1] = (byte)((p1 & 0xFF) + (uncompressed[i + 2] & 0xFF));
|
||||
p2 = lastRow[i + 2] = (byte)((p2 & 0xFF) + (uncompressed[i + 3] & 0xFF));
|
||||
p3 = lastRow[i + 3] = (byte)((p3 & 0xFF) + (uncompressed[i + 4] & 0xFF));
|
||||
}
|
||||
}
|
||||
|
||||
private static void filterAvg4(byte[] uncompressed, byte[] lastRow, int rowSize) {
|
||||
byte p0 = lastRow[0] = (byte)((uncompressed[1] & 0xFF) + ((lastRow[0] & 0xFF) >> 1));
|
||||
byte p1 = lastRow[1] = (byte)((uncompressed[2] & 0xFF) + ((lastRow[1] & 0xFF) >> 1));
|
||||
byte p2 = lastRow[2] = (byte)((uncompressed[3] & 0xFF) + ((lastRow[2] & 0xFF) >> 1));
|
||||
byte p3 = lastRow[3] = (byte)((uncompressed[4] & 0xFF) + ((lastRow[3] & 0xFF) >> 1));
|
||||
for (int i = 4; i < rowSize; i += 4) {
|
||||
p0 = lastRow[i] = (byte)(((lastRow[i] & 0xFF) + (p0 & 0xFF) >> 1) + (uncompressed[i + 1] & 0xFF));
|
||||
p1 = lastRow[i + 1] = (byte)(((lastRow[i + 1] & 0xFF) + (p1 & 0xFF) >> 1) + (uncompressed[i + 2] & 0xFF));
|
||||
p2 = lastRow[i + 2] = (byte)(((lastRow[i + 2] & 0xFF) + (p2 & 0xFF) >> 1) + (uncompressed[i + 3] & 0xFF));
|
||||
p3 = lastRow[i + 3] = (byte)(((lastRow[i + 3] & 0xFF) + (p3 & 0xFF) >> 1) + (uncompressed[i + 4] & 0xFF));
|
||||
}
|
||||
}
|
||||
|
||||
private static class PLTE {
|
||||
int[] palette;
|
||||
|
||||
public void parse(ByteBuffer data, int length) {
|
||||
if (length % 3 != 0 || length > 768)
|
||||
throw new RuntimeException("Invalid data");
|
||||
int n = length / 3;
|
||||
this.palette = new int[n];
|
||||
int i;
|
||||
for (i = 0; i < n; i++)
|
||||
this.palette[i] = 0xFF000000 | (data.get() & 0xFF) << 16 | (data.get() & 0xFF) << 8 |
|
||||
data.get() & 0xFF;
|
||||
for (; i < 256; i++)
|
||||
this.palette[i] = -16777216;
|
||||
data.getInt();
|
||||
}
|
||||
}
|
||||
|
||||
public static class TRNS {
|
||||
private int colorType;
|
||||
|
||||
byte[] alphaPal;
|
||||
|
||||
byte alphaGrey;
|
||||
|
||||
byte alphaR;
|
||||
|
||||
byte alphaG;
|
||||
|
||||
byte alphaB;
|
||||
|
||||
TRNS(byte colorType) {
|
||||
this.colorType = colorType;
|
||||
}
|
||||
|
||||
public void parse(ByteBuffer data, int length) {
|
||||
if (this.colorType == 3) {
|
||||
this.alphaPal = new byte[256];
|
||||
data.get(this.alphaPal, 0, length);
|
||||
for (int i = length; i < 256; i++)
|
||||
this.alphaPal[i] = -1;
|
||||
} else if (this.colorType == 0) {
|
||||
this.alphaGrey = data.get();
|
||||
} else if (this.colorType == 2) {
|
||||
this.alphaR = data.get();
|
||||
this.alphaG = data.get();
|
||||
this.alphaG = data.get();
|
||||
}
|
||||
data.getInt();
|
||||
}
|
||||
}
|
||||
|
||||
public VideoCodecMeta getCodecMeta(ByteBuffer _data) {
|
||||
ByteBuffer data = _data.duplicate();
|
||||
if (!ispng(data))
|
||||
throw new RuntimeException("Not a PNG file.");
|
||||
while (data.remaining() >= 8) {
|
||||
IHDR ihdr;
|
||||
int length = data.getInt();
|
||||
int tag = data.getInt();
|
||||
if (data.remaining() < length)
|
||||
break;
|
||||
switch (tag) {
|
||||
case 1229472850:
|
||||
ihdr = new IHDR();
|
||||
ihdr.parse(data);
|
||||
return VideoCodecMeta.createSimpleVideoCodecMeta(new Size(ihdr.width, ihdr.height), ColorSpace.RGB);
|
||||
}
|
||||
data.position(data.position() + length + 4);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean ispng(ByteBuffer data) {
|
||||
int sighi = data.getInt();
|
||||
int siglo = data.getInt();
|
||||
boolean ispng = ((sighi == -1991225785 || sighi == -1974645177) && (siglo == 218765834 || siglo == 218765834));
|
||||
return ispng;
|
||||
}
|
||||
|
||||
public static int probe(ByteBuffer data) {
|
||||
if (!ispng(data))
|
||||
return 100;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static byte[] deflate(byte[] data, Inflater inflater) throws DataFormatException {
|
||||
inflater.setInput(data);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(data.length);
|
||||
byte[] buffer = new byte[16384];
|
||||
while (!inflater.needsInput()) {
|
||||
int count = inflater.inflate(buffer);
|
||||
baos.write(buffer, 0, count);
|
||||
System.out.println(baos.size());
|
||||
}
|
||||
return baos.toByteArray();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
package org.jcodec.codecs.png;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.zip.CRC32;
|
||||
import java.util.zip.Deflater;
|
||||
import org.jcodec.common.VideoEncoder;
|
||||
import org.jcodec.common.io.NIOUtils;
|
||||
import org.jcodec.common.model.ColorSpace;
|
||||
import org.jcodec.common.model.Picture;
|
||||
|
||||
public class PNGEncoder extends VideoEncoder {
|
||||
private static int crc32(ByteBuffer from, ByteBuffer to) {
|
||||
from.limit(to.position());
|
||||
CRC32 crc32 = new CRC32();
|
||||
crc32.update(NIOUtils.toArray(from));
|
||||
return (int)crc32.getValue();
|
||||
}
|
||||
|
||||
public VideoEncoder.EncodedFrame encodeFrame(Picture pic, ByteBuffer out) {
|
||||
ByteBuffer _out = out.duplicate();
|
||||
_out.putInt(-1991225785);
|
||||
_out.putInt(218765834);
|
||||
IHDR ihdr = new IHDR();
|
||||
ihdr.width = pic.getCroppedWidth();
|
||||
ihdr.height = pic.getCroppedHeight();
|
||||
ihdr.bitDepth = 8;
|
||||
ihdr.colorType = 2;
|
||||
_out.putInt(13);
|
||||
ByteBuffer crcFrom = _out.duplicate();
|
||||
_out.putInt(1229472850);
|
||||
ihdr.write(_out);
|
||||
_out.putInt(crc32(crcFrom, _out));
|
||||
Deflater deflater = new Deflater();
|
||||
byte[] rowData = new byte[pic.getCroppedWidth() * 3 + 1];
|
||||
byte[] pix = pic.getPlaneData(0);
|
||||
byte[] buffer = new byte[32768];
|
||||
int ptr = 0, len = buffer.length;
|
||||
int lineStep = (pic.getWidth() - pic.getCroppedWidth()) * 3;
|
||||
for (int row = 0, bptr = 0; row < pic.getCroppedHeight() + 1; row++) {
|
||||
int count;
|
||||
while ((count = deflater.deflate(buffer, ptr, len)) > 0) {
|
||||
ptr += count;
|
||||
len -= count;
|
||||
if (len == 0) {
|
||||
_out.putInt(ptr);
|
||||
crcFrom = _out.duplicate();
|
||||
_out.putInt(1229209940);
|
||||
_out.put(buffer, 0, ptr);
|
||||
_out.putInt(crc32(crcFrom, _out));
|
||||
ptr = 0;
|
||||
len = buffer.length;
|
||||
}
|
||||
}
|
||||
if (row >= pic.getCroppedHeight())
|
||||
break;
|
||||
rowData[0] = 0;
|
||||
for (int i = 1; i <= pic.getCroppedWidth() * 3; i += 3, bptr += 3) {
|
||||
rowData[i] = (byte)(pix[bptr] + 128);
|
||||
rowData[i + 1] = (byte)(pix[bptr + 1] + 128);
|
||||
rowData[i + 2] = (byte)(pix[bptr + 2] + 128);
|
||||
}
|
||||
bptr += lineStep;
|
||||
deflater.setInput(rowData);
|
||||
if (row >= pic.getCroppedHeight() - 1)
|
||||
deflater.finish();
|
||||
}
|
||||
if (ptr > 0) {
|
||||
_out.putInt(ptr);
|
||||
crcFrom = _out.duplicate();
|
||||
_out.putInt(1229209940);
|
||||
_out.put(buffer, 0, ptr);
|
||||
_out.putInt(crc32(crcFrom, _out));
|
||||
}
|
||||
_out.putInt(0);
|
||||
_out.putInt(1229278788);
|
||||
_out.putInt(-1371381630);
|
||||
_out.flip();
|
||||
return new VideoEncoder.EncodedFrame(_out, true);
|
||||
}
|
||||
|
||||
public ColorSpace[] getSupportedColorSpaces() {
|
||||
return new ColorSpace[] { ColorSpace.RGB };
|
||||
}
|
||||
|
||||
public int estimateBufferSize(Picture frame) {
|
||||
return frame.getCroppedWidth() * frame.getCroppedHeight() * 4;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue