WebP initial commit
32
imageio/imageio-webp/pom.xml
Normal file
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio</artifactId>
|
||||
<version>3.7-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>imageio-webp</artifactId>
|
||||
<name>TwelveMonkeys :: ImageIO :: WebP plugin</name>
|
||||
<description>
|
||||
ImageIO plugin for Google WebP File Format (WebP).
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-metadata</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.twelvemonkeys.imageio</groupId>
|
||||
<artifactId>imageio-core</artifactId>
|
||||
<type>test-jar</type>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.webp;
|
||||
|
||||
/**
|
||||
* A generic RIFF chunk
|
||||
*/
|
||||
final class GenericChunk extends RIFFChunk {
|
||||
GenericChunk(int fourCC, int length, long offset) {
|
||||
super(fourCC, length, offset);
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package com.twelvemonkeys.imageio.plugins.webp;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* LSBBitReader
|
||||
*/
|
||||
public final class LSBBitReader {
|
||||
// TODO: Consider creating an ImageInputStream wrapper with the WebP implementation of readBit(s)?
|
||||
|
||||
private final ImageInputStream imageInput;
|
||||
int bitOffset = 0;
|
||||
|
||||
public LSBBitReader(ImageInputStream imageInput) {
|
||||
this.imageInput = imageInput;
|
||||
}
|
||||
|
||||
// TODO: Optimize this... Read many bits at once!
|
||||
public long readBits(int bits) throws IOException {
|
||||
long result = 0;
|
||||
for (int i = 0; i < bits; i++) {
|
||||
result |= readBit() << i;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO: Optimize this...
|
||||
// TODO: Consider not reading value over and over....
|
||||
public int readBit() throws IOException {
|
||||
int bit = 7 - bitOffset;
|
||||
|
||||
imageInput.setBitOffset(bit);
|
||||
|
||||
// Compute final bit offset before we call read() and seek()
|
||||
int newBitOffset = (bitOffset + 1) & 0x7;
|
||||
|
||||
int val = imageInput.read();
|
||||
if (val == -1) {
|
||||
throw new EOFException();
|
||||
}
|
||||
|
||||
if (newBitOffset != 0) {
|
||||
// Move byte position back if in the middle of a byte
|
||||
// NOTE: RESETS bit offset!
|
||||
imageInput.seek(imageInput.getStreamPosition() - 1);
|
||||
}
|
||||
|
||||
bitOffset = newBitOffset;
|
||||
|
||||
// Shift the bit to be read to the rightmost position
|
||||
return (val >> (7 - bit)) & 0x1;
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.webp;
|
||||
|
||||
import static com.twelvemonkeys.imageio.plugins.webp.WebPImageReader.fourCC;
|
||||
|
||||
/**
|
||||
* An abstract RIFF chunk.
|
||||
* <p/>
|
||||
* RIFF was introduced in 1991 by Microsoft and IBM, and was presented
|
||||
* by Microsoft as the default format for Windows 3.1 multimedia files. It is
|
||||
* based on Electronic Arts' Interchange File Format, introduced in 1985 on
|
||||
* the Commodore Amiga, the only difference being that multi-byte integers are
|
||||
* in little-endian format, native to the 80x86 processor series used in
|
||||
* IBM PCs, rather than the big-endian format native to the 68k processor
|
||||
* series used in Amiga and Apple Macintosh computers, where IFF files were
|
||||
* heavily used.
|
||||
* <p/>
|
||||
* In 2010 Google introduced the WebP picture format, which uses RIFF as a
|
||||
* container.
|
||||
*
|
||||
* @see <a href="https://en.wikipedia.org/wiki/Resource_Interchange_File_Format">Resource Interchange Format</a>
|
||||
*/
|
||||
abstract class RIFFChunk {
|
||||
|
||||
final int fourCC;
|
||||
final long length;
|
||||
final long offset;
|
||||
|
||||
RIFFChunk(int fourCC, long length, long offset) {
|
||||
this.fourCC = fourCC;
|
||||
this.length = length;
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return fourCC(fourCC).replace(' ', '_')
|
||||
+ "Chunk@" + offset + "|" + length;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
package com.twelvemonkeys.imageio.plugins.webp;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.*;
|
||||
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
|
||||
/**
|
||||
* RasterUtils
|
||||
*/
|
||||
public final class RasterUtils {
|
||||
// TODO: Generalize and move to common util package
|
||||
|
||||
private RasterUtils() {}
|
||||
|
||||
public static WritableRaster asByteRaster(final WritableRaster raster, final ColorModel colorModel) {
|
||||
switch (raster.getTransferType()) {
|
||||
case DataBuffer.TYPE_BYTE:
|
||||
return raster;
|
||||
case DataBuffer.TYPE_INT:
|
||||
final int bands = colorModel.getNumComponents();
|
||||
final DataBufferInt buffer = (DataBufferInt) raster.getDataBuffer();
|
||||
|
||||
int w = raster.getWidth();
|
||||
int h = raster.getHeight();
|
||||
int size = buffer.getSize();
|
||||
|
||||
return new WritableRaster(
|
||||
new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, w, h, bands, w * bands, createBandOffsets(colorModel)),
|
||||
new DataBuffer(DataBuffer.TYPE_BYTE, size * bands) {
|
||||
// TODO: These masks should probably not be hardcoded
|
||||
final int[] MASKS = {
|
||||
0xffffff00,
|
||||
0xffff00ff,
|
||||
0xff00ffff,
|
||||
0x00ffffff,
|
||||
};
|
||||
|
||||
@Override
|
||||
public int getElem(int bank, int i) {
|
||||
int index = i / bands;
|
||||
int shift = (i % bands) * 8;
|
||||
|
||||
return (buffer.getElem(index) >>> shift) & 0xff;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setElem(int bank, int i, int val) {
|
||||
int index = i / bands;
|
||||
int element = i % bands;
|
||||
int shift = element * 8;
|
||||
|
||||
int value = (buffer.getElem(index) & MASKS[element]) | ((val & 0xff) << shift);
|
||||
buffer.setElem(index, value);
|
||||
}
|
||||
}, new Point()) {};
|
||||
default:
|
||||
throw new IllegalArgumentException(String.format("Raster type %d not supported", raster.getTransferType()));
|
||||
}
|
||||
}
|
||||
|
||||
private static int[] createBandOffsets(final ColorModel colorModel) {
|
||||
notNull(colorModel, "colorModel");
|
||||
|
||||
if (colorModel instanceof DirectColorModel) {
|
||||
DirectColorModel dcm = (DirectColorModel) colorModel;
|
||||
int[] masks = dcm.getMasks();
|
||||
int[] offs = new int[masks.length];
|
||||
|
||||
for (int i = 0; i < masks.length; i++) {
|
||||
int mask = masks[i];
|
||||
int off = 0;
|
||||
|
||||
// TODO: FixMe! This only works for standard 8 bit masks (0xFF)
|
||||
if (mask != 0) {
|
||||
while ((mask & 0xFF) == 0) {
|
||||
mask >>>= 8;
|
||||
off++;
|
||||
}
|
||||
}
|
||||
|
||||
offs[i] = off;
|
||||
}
|
||||
|
||||
return offs;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(String.format("%s not supported", colorModel.getClass().getSimpleName()));
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.webp;
|
||||
|
||||
/**
|
||||
* A special VP8* RIFF chunk, used as the first chunk of a WebP file.
|
||||
*/
|
||||
final class VP8xChunk extends RIFFChunk {
|
||||
int width;
|
||||
int height;
|
||||
|
||||
boolean isLossless;
|
||||
|
||||
boolean containsICCP;
|
||||
boolean containsALPH;
|
||||
boolean containsEXIF;
|
||||
boolean containsXMP_;
|
||||
boolean containsANIM;
|
||||
|
||||
VP8xChunk(int fourCC, long length, long offset) {
|
||||
super(fourCC, length, offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "[" +
|
||||
"width=" + width +
|
||||
", height=" + height +
|
||||
", lossless=" + (isLossless ? "RGB" : "")
|
||||
+ (containsALPH ? "A" : (isLossless ? "" : "false")) +
|
||||
", flags=" +
|
||||
(containsICCP ? "I" : "") +
|
||||
(containsALPH ? "L" : "") +
|
||||
(containsEXIF ? "E" : "") +
|
||||
(containsXMP_ ? "X" : "") +
|
||||
(containsANIM ? "A" : "") +
|
||||
']';
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.webp;
|
||||
|
||||
/**
|
||||
* WebP
|
||||
*/
|
||||
interface WebP {
|
||||
int RIFF_MAGIC = 'R' | 'I' << 8 | 'F' << 16 | 'F' << 24;
|
||||
int WEBP_MAGIC = 'W' | 'E' << 8 | 'B' << 16 | 'P' << 24;
|
||||
|
||||
int CHUNK_VP8_ = 'V' | 'P' << 8 | '8' << 16 | ' ' << 24;
|
||||
int CHUNK_VP8L = 'V' | 'P' << 8 | '8' << 16 | 'L' << 24;
|
||||
int CHUNK_VP8X = 'V' | 'P' << 8 | '8' << 16 | 'X' << 24;
|
||||
|
||||
int CHUNK_ALPH = 'A' | 'L' << 8 | 'P' << 16 | 'H' << 24;
|
||||
int CHUNK_ANIM = 'A' | 'N' << 8 | 'I' << 16 | 'M' << 24;
|
||||
int CHUNK_ANMF = 'A' | 'N' << 8 | 'M' << 16 | 'F' << 24;
|
||||
int CHUNK_ICCP = 'I' | 'C' << 8 | 'C' << 16 | 'P' << 24;
|
||||
int CHUNK_EXIF = 'E' | 'X' << 8 | 'I' << 16 | 'F' << 24;
|
||||
int CHUNK_XMP_ = 'X' | 'M' << 8 | 'P' << 16 | ' ' << 24;
|
||||
|
||||
byte LOSSLESSS_SIG = 0x2f;
|
||||
}
|
@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.webp;
|
||||
|
||||
import com.twelvemonkeys.imageio.AbstractMetadata;
|
||||
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
|
||||
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||
|
||||
/**
|
||||
* WebPMetadata
|
||||
*/
|
||||
final class WebPImageMetadata extends AbstractMetadata {
|
||||
private final VP8xChunk header;
|
||||
|
||||
WebPImageMetadata(final VP8xChunk header) {
|
||||
this.header = notNull(header, "header");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardChromaNode() {
|
||||
IIOMetadataNode chroma = new IIOMetadataNode("Chroma");
|
||||
|
||||
IIOMetadataNode csType = new IIOMetadataNode("ColorSpaceType");
|
||||
chroma.appendChild(csType);
|
||||
csType.setAttribute("name", "RGB");
|
||||
|
||||
// NOTE: Channels in chroma node reflects channels in color model (see data node, for channels in data)
|
||||
IIOMetadataNode numChannels = new IIOMetadataNode("NumChannels");
|
||||
chroma.appendChild(numChannels);
|
||||
numChannels.setAttribute("value", Integer.toString(header.containsALPH ? 4 : 3));
|
||||
|
||||
IIOMetadataNode blackIsZero = new IIOMetadataNode("BlackIsZero");
|
||||
chroma.appendChild(blackIsZero);
|
||||
blackIsZero.setAttribute("value", "TRUE");
|
||||
|
||||
return chroma;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardCompressionNode() {
|
||||
IIOMetadataNode node = new IIOMetadataNode("Compression");
|
||||
|
||||
IIOMetadataNode compressionTypeName = new IIOMetadataNode("CompressionTypeName");
|
||||
node.appendChild(compressionTypeName);
|
||||
|
||||
String value = header.isLossless ? "VP8L" : "VP8"; // TODO: Naming: VP8L and VP8 or WebP and WebP Lossless?
|
||||
compressionTypeName.setAttribute("value", value);
|
||||
|
||||
// TODO: VP8 + lossless alpha!
|
||||
IIOMetadataNode lossless = new IIOMetadataNode("Lossless");
|
||||
node.appendChild(lossless);
|
||||
lossless.setAttribute("value", header.isLossless ? "TRUE" : "FALSE");
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDataNode() {
|
||||
IIOMetadataNode node = new IIOMetadataNode("Data");
|
||||
|
||||
// TODO: WebP seems to support planar as well?
|
||||
IIOMetadataNode planarConfiguration = new IIOMetadataNode("PlanarConfiguration");
|
||||
node.appendChild(planarConfiguration);
|
||||
planarConfiguration.setAttribute("value", "PixelInterleaved");
|
||||
|
||||
IIOMetadataNode sampleFormat = new IIOMetadataNode("SampleFormat");
|
||||
node.appendChild(sampleFormat);
|
||||
sampleFormat.setAttribute("value", "UnsignedIntegral");
|
||||
|
||||
IIOMetadataNode bitsPerSample = new IIOMetadataNode("BitsPerSample");
|
||||
node.appendChild(bitsPerSample);
|
||||
|
||||
bitsPerSample.setAttribute("value", createListValue(header.containsALPH ? 4 : 3, Integer.toString(8)));
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private String createListValue(final int itemCount, final String... values) {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < itemCount; i++) {
|
||||
if (buffer.length() > 0) {
|
||||
buffer.append(' ');
|
||||
}
|
||||
|
||||
buffer.append(values[i % values.length]);
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDimensionNode() {
|
||||
IIOMetadataNode dimension = new IIOMetadataNode("Dimension");
|
||||
|
||||
IIOMetadataNode imageOrientation = new IIOMetadataNode("ImageOrientation");
|
||||
dimension.appendChild(imageOrientation);
|
||||
imageOrientation.setAttribute("value", "Normal");
|
||||
|
||||
IIOMetadataNode pixelAspectRatio = new IIOMetadataNode("PixelAspectRatio");
|
||||
dimension.appendChild(pixelAspectRatio);
|
||||
pixelAspectRatio.setAttribute("value", "1.0");
|
||||
|
||||
return dimension;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardDocumentNode() {
|
||||
IIOMetadataNode document = new IIOMetadataNode("Document");
|
||||
|
||||
IIOMetadataNode formatVersion = new IIOMetadataNode("FormatVersion");
|
||||
document.appendChild(formatVersion);
|
||||
formatVersion.setAttribute("value", "1.0");
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardTextNode() {
|
||||
IIOMetadataNode text = new IIOMetadataNode("Text");
|
||||
|
||||
// TODO: Get useful text nodes from EXIF or XMP
|
||||
// NOTE: Names corresponds to equivalent fields in TIFF
|
||||
|
||||
return text.hasChildNodes() ? text : null;
|
||||
}
|
||||
|
||||
// private void appendTextEntry(final IIOMetadataNode parent, final String keyword, final String value) {
|
||||
// if (value != null) {
|
||||
// IIOMetadataNode textEntry = new IIOMetadataNode("TextEntry");
|
||||
// parent.appendChild(textEntry);
|
||||
// textEntry.setAttribute("keyword", keyword);
|
||||
// textEntry.setAttribute("value", value);
|
||||
// }
|
||||
// }
|
||||
|
||||
// No tiling
|
||||
|
||||
@Override
|
||||
protected IIOMetadataNode getStandardTransparencyNode() {
|
||||
if (header.containsALPH) {
|
||||
IIOMetadataNode transparency = new IIOMetadataNode("Transparency");
|
||||
IIOMetadataNode alpha = new IIOMetadataNode("Alpha");
|
||||
transparency.appendChild(alpha);
|
||||
alpha.setAttribute("value", "nonpremultiplied");
|
||||
return transparency;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: Define native WebP metadata format (probably use RIFF structure)
|
||||
}
|
@ -0,0 +1,514 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.webp;
|
||||
|
||||
import com.twelvemonkeys.imageio.ImageReaderBase;
|
||||
import com.twelvemonkeys.imageio.metadata.Directory;
|
||||
import com.twelvemonkeys.imageio.metadata.tiff.TIFFReader;
|
||||
import com.twelvemonkeys.imageio.metadata.xmp.XMPReader;
|
||||
import com.twelvemonkeys.imageio.plugins.webp.lossless.VP8LDecoder;
|
||||
import com.twelvemonkeys.imageio.plugins.webp.vp8.VP8Frame;
|
||||
import com.twelvemonkeys.imageio.stream.SubImageInputStream;
|
||||
import com.twelvemonkeys.imageio.util.IIOUtil;
|
||||
import com.twelvemonkeys.imageio.util.ImageTypeSpecifiers;
|
||||
import com.twelvemonkeys.imageio.util.ProgressListenerBase;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.ImageReadParam;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.ImageTypeSpecifier;
|
||||
import javax.imageio.metadata.IIOMetadata;
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.color.ICC_Profile;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.WritableRaster;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* WebPImageReader
|
||||
*/
|
||||
final class WebPImageReader extends ImageReaderBase {
|
||||
|
||||
final static boolean DEBUG = "true".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.imageio.plugins.webp.debug"));
|
||||
|
||||
private LSBBitReader lsbBitReader;
|
||||
|
||||
// Either VP8_, VP8L or VP8X chunk
|
||||
private VP8xChunk header;
|
||||
private ICC_Profile iccProfile;
|
||||
|
||||
WebPImageReader(ImageReaderSpi provider) {
|
||||
super(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void resetMembers() {
|
||||
header = null;
|
||||
iccProfile = null;
|
||||
lsbBitReader = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInput(Object input, boolean seekForwardOnly, boolean ignoreMetadata) {
|
||||
super.setInput(input, seekForwardOnly, ignoreMetadata);
|
||||
|
||||
lsbBitReader = new LSBBitReader(imageInput);
|
||||
}
|
||||
|
||||
private void readHeader(int imageIndex) throws IOException {
|
||||
checkBounds(imageIndex);
|
||||
|
||||
// TODO: Consider just storing the chunks, parse until VP8, VP8L or VP8X chunk
|
||||
if (header != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Generalize RIFF chunk parsing!
|
||||
|
||||
// RIFF native order is Little Endian
|
||||
imageInput.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
imageInput.seek(0);
|
||||
|
||||
int riff = imageInput.readInt();
|
||||
if (riff != WebP.RIFF_MAGIC) {
|
||||
throw new IIOException(String.format("Not a WebP file, invalid 'RIFF' magic: '%s'", fourCC(riff)));
|
||||
}
|
||||
|
||||
imageInput.readUnsignedInt(); // Skip file size NOTE: LITTLE endian!
|
||||
|
||||
int webp = imageInput.readInt();
|
||||
if (webp != WebP.WEBP_MAGIC) {
|
||||
throw new IIOException(String.format("Not a WebP file, invalid 'WEBP' magic: '%s'", fourCC(webp)));
|
||||
}
|
||||
|
||||
int chunk = imageInput.readInt();
|
||||
long chunkLen = imageInput.readUnsignedInt();
|
||||
|
||||
header = new VP8xChunk(chunk, chunkLen, imageInput.getStreamPosition());
|
||||
|
||||
switch (chunk) {
|
||||
case WebP.CHUNK_VP8_:
|
||||
//https://tools.ietf.org/html/rfc6386#section-9.1
|
||||
int frameType = lsbBitReader.readBit(); // 0 = key frame, 1 = interframe (not used in WebP)
|
||||
|
||||
if (frameType != 0) {
|
||||
throw new IIOException("Unexpected WebP frame type (expected 0): " + frameType);
|
||||
}
|
||||
|
||||
int versionNumber = (int) lsbBitReader.readBits(3); // 0 - 3 = different profiles (see spec)
|
||||
int showFrame = lsbBitReader.readBit(); // 0 = don't show, 1 = show
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.println("versionNumber: " + versionNumber);
|
||||
System.out.println("showFrame: " + showFrame);
|
||||
}
|
||||
|
||||
// 19 bit field containing the size of the first data partition in bytes
|
||||
lsbBitReader.readBits(19);
|
||||
|
||||
// StartCode 0, 1, 2
|
||||
imageInput.readUnsignedByte();
|
||||
imageInput.readUnsignedByte();
|
||||
imageInput.readUnsignedByte();
|
||||
|
||||
// (2 bits Horizontal Scale << 14) | Width (14 bits)
|
||||
int hBytes = imageInput.readUnsignedShort();
|
||||
header.width = (hBytes & 0x3fff);
|
||||
|
||||
// (2 bits Vertical Scale << 14) | Height (14 bits)
|
||||
int vBytes = imageInput.readUnsignedShort();
|
||||
header.height = (vBytes & 0x3fff);
|
||||
|
||||
break;
|
||||
|
||||
case WebP.CHUNK_VP8L:
|
||||
byte signature = imageInput.readByte();
|
||||
if (signature != WebP.LOSSLESSS_SIG) {
|
||||
throw new IIOException(String.format("Unexpected 'VP8L' signature, expected 0x0x%2x: 0x%2x", WebP.LOSSLESSS_SIG, signature));
|
||||
}
|
||||
|
||||
header.isLossless = true;
|
||||
|
||||
// 14 bit width, 14 bit height, 1 bit alpha, 3 bit version
|
||||
header.width = 1 + (int) lsbBitReader.readBits(14);
|
||||
header.height = 1 + (int) lsbBitReader.readBits(14);
|
||||
header.containsALPH = lsbBitReader.readBit() == 1;
|
||||
|
||||
int version = (int) lsbBitReader.readBits(3);
|
||||
|
||||
if (version != 0) {
|
||||
throw new IIOException(String.format("Unexpected 'VP8L' version, expected 0: %d", version));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case WebP.CHUNK_VP8X:
|
||||
if (chunkLen != 10) {
|
||||
throw new IIOException("Unexpected 'VP8X' chunk length, expected 10: " + chunkLen);
|
||||
}
|
||||
|
||||
// RsV|I|L|E|X|A|R
|
||||
int reserved = (int) imageInput.readBits(2);
|
||||
if (reserved != 0) {
|
||||
// Spec says SHOULD be 0
|
||||
throw new IIOException(String.format("Unexpected 'VP8X' chunk reserved value, expected 0: %d", reserved));
|
||||
}
|
||||
|
||||
header.containsICCP = imageInput.readBit() == 1;
|
||||
header.containsALPH = imageInput.readBit() == 1; // L -> aLpha
|
||||
header.containsEXIF = imageInput.readBit() == 1;
|
||||
header.containsXMP_ = imageInput.readBit() == 1;
|
||||
header.containsANIM = imageInput.readBit() == 1; // A -> Anim
|
||||
|
||||
reserved = (int) imageInput.readBits(25); // 1 + 24 bits reserved
|
||||
if (reserved != 0) {
|
||||
// Spec says SHOULD be 0
|
||||
throw new IIOException(String.format("Unexpected 'VP8X' chunk reserved value, expected 0: %d", reserved));
|
||||
}
|
||||
|
||||
// NOTE: Spec refers to this as *Canvas* size, as opposed to *Image* size for the lossless chunk
|
||||
header.width = 1 + (int) lsbBitReader.readBits(24);
|
||||
header.height = 1 + (int) lsbBitReader.readBits(24);
|
||||
|
||||
if (header.containsICCP) {
|
||||
// ICCP chunk must be first chunk, if present
|
||||
while (iccProfile != null && imageInput.getStreamPosition() < imageInput.length()) {
|
||||
int nextChunk = imageInput.readInt();
|
||||
long chunkLength = imageInput.readUnsignedInt();
|
||||
long chunkStart = imageInput.getStreamPosition();
|
||||
|
||||
if (nextChunk == WebP.CHUNK_ICCP) {
|
||||
iccProfile = ICC_Profile.getInstance(IIOUtil.createStreamAdapter(imageInput, chunkLength));
|
||||
}
|
||||
else {
|
||||
processWarningOccurred(String.format("Expected 'ICCP' chunk, '%s' chunk encountered", fourCC(nextChunk)));
|
||||
}
|
||||
|
||||
imageInput.seek(chunkStart + chunkLength + (chunkLength & 1)); // Padded to even length
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IIOException(String.format("Unknown WebP chunk: '%s'", fourCC(chunk)));
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.println("header: " + header);
|
||||
}
|
||||
}
|
||||
|
||||
static String fourCC(int value) {
|
||||
// NOTE: Little Endian
|
||||
return new String(
|
||||
new byte[]{
|
||||
(byte) ((value & 0x000000ff) ),
|
||||
(byte) ((value & 0x0000ff00) >> 8),
|
||||
(byte) ((value & 0x00ff0000) >> 16),
|
||||
(byte) ((value & 0xff000000) >>> 24),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumImages(boolean allowSearch) throws IOException {
|
||||
// TODO: Support ANIM/ANMF
|
||||
return super.getNumImages(allowSearch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth(int imageIndex) throws IOException {
|
||||
readHeader(imageIndex);
|
||||
return header.width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight(int imageIndex) throws IOException {
|
||||
readHeader(imageIndex);
|
||||
return header.height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageTypeSpecifier getRawImageType(int imageIndex) throws IOException {
|
||||
readHeader(imageIndex);
|
||||
|
||||
// TODO: Spec says:
|
||||
// "alpha value is codified in bits 31..24, red in bits 23..16, green in bits 15..8 and blue in bits 7..0,
|
||||
// but implementations of the format are free to use another representation internally."
|
||||
// TODO: Doc says alpha flag is "hint only" :-P
|
||||
// TODO: ICC profile?!
|
||||
return ImageTypeSpecifiers.createFromBufferedImageType(header.containsALPH ? BufferedImage.TYPE_4BYTE_ABGR : BufferedImage.TYPE_3BYTE_BGR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ImageTypeSpecifier> getImageTypes(int imageIndex) throws IOException {
|
||||
ImageTypeSpecifier rawImageType = getRawImageType(imageIndex);
|
||||
|
||||
List<ImageTypeSpecifier> types = new ArrayList<>();
|
||||
types.add(rawImageType);
|
||||
types.add(ImageTypeSpecifiers.createFromBufferedImageType(header.containsALPH ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB));
|
||||
|
||||
return types.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException {
|
||||
int width = getWidth(imageIndex);
|
||||
int height = getHeight(imageIndex);
|
||||
BufferedImage destination = getDestination(param, getImageTypes(imageIndex), width, height);
|
||||
|
||||
processImageStarted(imageIndex);
|
||||
|
||||
switch (header.fourCC) {
|
||||
case WebP.CHUNK_VP8_:
|
||||
imageInput.seek(header.offset);
|
||||
readVP8(RasterUtils.asByteRaster(destination.getRaster(), destination.getColorModel()));
|
||||
|
||||
break;
|
||||
|
||||
case WebP.CHUNK_VP8L:
|
||||
imageInput.seek(header.offset);
|
||||
readVP8Lossless(RasterUtils.asByteRaster(destination.getRaster(), destination.getColorModel()));
|
||||
|
||||
break;
|
||||
|
||||
case WebP.CHUNK_VP8X:
|
||||
imageInput.seek(header.offset + header.length);
|
||||
|
||||
while (imageInput.getStreamPosition() < imageInput.length()) {
|
||||
int nextChunk = imageInput.readInt();
|
||||
long chunkLength = imageInput.readUnsignedInt();
|
||||
long chunkStart = imageInput.getStreamPosition();
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.printf("chunk: '%s'\n", fourCC(nextChunk));
|
||||
System.out.println("chunkLength: " + chunkLength);
|
||||
System.out.println("chunkStart: " + chunkStart);
|
||||
}
|
||||
|
||||
switch (nextChunk) {
|
||||
case WebP.CHUNK_ALPH:
|
||||
int reserved = (int) imageInput.readBits(2);
|
||||
if (reserved != 0) {
|
||||
// Spec says SHOULD be 0
|
||||
throw new IIOException(String.format("Unexpected 'ALPH' chunk reserved value, expected 0: %d", reserved));
|
||||
}
|
||||
|
||||
int preProcessing = (int) imageInput.readBits(2);
|
||||
int filtering = (int) imageInput.readBits(2);
|
||||
int compression = (int) imageInput.readBits(2);
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.println("preProcessing: " + preProcessing);
|
||||
System.out.println("filtering: " + filtering);
|
||||
System.out.println("compression: " + compression);
|
||||
}
|
||||
|
||||
switch (compression) {
|
||||
case 0:
|
||||
readUncompressedAlpha(destination.getAlphaRaster());
|
||||
break;
|
||||
case 1:
|
||||
opaqueAlpha(destination.getAlphaRaster()); // TODO: Remove when correctly implemented!
|
||||
readVP8Lossless(destination.getAlphaRaster());
|
||||
break;
|
||||
default:
|
||||
processWarningOccurred("Unknown WebP alpha compression: " + compression);
|
||||
opaqueAlpha(destination.getAlphaRaster());
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case WebP.CHUNK_VP8_:
|
||||
readVP8(RasterUtils.asByteRaster(destination.getRaster(), destination.getColorModel())
|
||||
.createWritableChild(0, 0, width, height, 0, 0, new int[]{0, 1, 2}));
|
||||
|
||||
break;
|
||||
|
||||
case WebP.CHUNK_VP8L:
|
||||
readVP8Lossless(RasterUtils.asByteRaster(destination.getRaster(), destination.getColorModel()));
|
||||
|
||||
break;
|
||||
|
||||
case WebP.CHUNK_ANIM:
|
||||
case WebP.CHUNK_ANMF:
|
||||
processWarningOccurred("Ignoring unsupported chunk: " + fourCC(nextChunk));
|
||||
break;
|
||||
default:
|
||||
processWarningOccurred("Ignoring unexpected chunk: " + fourCC(nextChunk));
|
||||
break;
|
||||
}
|
||||
|
||||
imageInput.seek(chunkStart + chunkLength + (chunkLength & 1)); // Padded to even length
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IIOException("Unknown first chunk for WebP: " + fourCC(header.fourCC));
|
||||
}
|
||||
|
||||
if (abortRequested()) {
|
||||
processReadAborted();
|
||||
} else {
|
||||
processImageComplete();
|
||||
}
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
private void opaqueAlpha(final WritableRaster alphaRaster) {
|
||||
int h = alphaRaster.getHeight();
|
||||
int w = alphaRaster.getWidth();
|
||||
|
||||
for (int y = 0; y < h; y++) {
|
||||
for (int x = 0; x < w; x++) {
|
||||
alphaRaster.setSample(x, y, 0, 0xff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readUncompressedAlpha(final WritableRaster alphaRaster) throws IOException {
|
||||
// Hardly used in practice, need to find a sample file
|
||||
processWarningOccurred("Uncompressed WebP alpha not implemented");
|
||||
opaqueAlpha(alphaRaster);
|
||||
}
|
||||
|
||||
private void readVP8Lossless(final WritableRaster raster) throws IOException {
|
||||
VP8LDecoder decoder = new VP8LDecoder(imageInput);
|
||||
decoder.readVP8Lossless(raster, true);
|
||||
}
|
||||
|
||||
private void readVP8(final WritableRaster raster) throws IOException {
|
||||
VP8Frame frame = new VP8Frame(imageInput);
|
||||
|
||||
frame.setProgressListener(new ProgressListenerBase() {
|
||||
@Override
|
||||
public void imageProgress(ImageReader source, float percentageDone) {
|
||||
processImageProgress(percentageDone);
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Consider merging these operations...
|
||||
if (frame.decodeFrame(false)) {
|
||||
frame.copyTo(raster);
|
||||
}
|
||||
}
|
||||
|
||||
// Metadata
|
||||
|
||||
@Override
|
||||
public IIOMetadata getImageMetadata(int imageIndex) throws IOException {
|
||||
readHeader(imageIndex);
|
||||
// TODO: Read possible EXIF and 'XMP ' chunks
|
||||
readMeta();
|
||||
|
||||
|
||||
return new WebPImageMetadata(header);
|
||||
}
|
||||
|
||||
private void readMeta() throws IOException {
|
||||
if (header.containsEXIF || header.containsXMP_) {
|
||||
// TODO: WebP spec says possible EXIF and XMP chunks are always AFTER image data
|
||||
imageInput.seek(header.offset + header.length);
|
||||
|
||||
while (imageInput.getStreamPosition() < imageInput.length()) {
|
||||
int nextChunk = imageInput.readInt();
|
||||
long chunkLength = imageInput.readUnsignedInt();
|
||||
long chunkStart = imageInput.getStreamPosition();
|
||||
|
||||
// System.err.printf("chunk: '%s'\n", fourCC(nextChunk));
|
||||
// System.err.println("chunkLength: " + chunkLength);
|
||||
// System.err.println("chunkStart: " + chunkStart);
|
||||
|
||||
switch (nextChunk) {
|
||||
case WebP.CHUNK_EXIF:
|
||||
// TODO: Figure out if the following is correct
|
||||
// The (only) sample image contains 'Exif\0\0', just like the JPEG APP1/Exif segment...
|
||||
// However, I cannot see this documented anywhere? Probably this is bogus...
|
||||
// For now, we'll support both cases for compatibility.
|
||||
int skippedCount = 0;
|
||||
byte[] bytes = new byte[6];
|
||||
imageInput.readFully(bytes);
|
||||
if (bytes[0] == 'E' && bytes[1] == 'x' && bytes[2] == 'i' && bytes[3] == 'f' && bytes[4] == 0 && bytes[5] == 0) {
|
||||
skippedCount = 6;
|
||||
}
|
||||
else {
|
||||
imageInput.seek(chunkStart);
|
||||
}
|
||||
|
||||
SubImageInputStream input = new SubImageInputStream(imageInput, chunkLength - skippedCount);
|
||||
Directory exif = new TIFFReader().read(input);
|
||||
|
||||
// Entry jpegOffsetTag = exif.getEntryById(TIFF.TAG_JPEG_INTERCHANGE_FORMAT);
|
||||
// if (jpegOffsetTag != null) {
|
||||
// // Wohoo.. There's a JPEG inside the EIXF inside the WEBP...
|
||||
// long jpegOffset = ((Number) jpegOffsetTag.getValue()).longValue();
|
||||
// input.seek(jpegOffset);
|
||||
// BufferedImage thumb = ImageIO.read(new SubImageInputStream(input, chunkLength - jpegOffset));
|
||||
// System.err.println("thumb: " + thumb);
|
||||
// showIt(thumb, "EXIF thumb");
|
||||
// }
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.println("exif: " + exif);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case WebP.CHUNK_XMP_:
|
||||
Directory xmp = new XMPReader().read(new SubImageInputStream(imageInput, chunkLength));
|
||||
|
||||
if (DEBUG) {
|
||||
System.out.println("xmp: " + xmp);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
imageInput.seek(chunkStart + chunkLength + (chunkLength & 1)); // Padded to even length
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static void showIt(BufferedImage image, String title) {
|
||||
ImageReaderBase.showIt(image, title);
|
||||
}
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.webp;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
|
||||
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* WebPImageReaderSpi
|
||||
*/
|
||||
public final class WebPImageReaderSpi extends ImageReaderSpiBase {
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public WebPImageReaderSpi() {
|
||||
super(new WebPProviderInfo());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDecodeInput(final Object source) throws IOException {
|
||||
return source instanceof ImageInputStream && canDecode((ImageInputStream) source);
|
||||
}
|
||||
|
||||
private static boolean canDecode(final ImageInputStream stream) throws IOException {
|
||||
ByteOrder originalOrder = stream.getByteOrder();
|
||||
stream.mark();
|
||||
|
||||
try {
|
||||
// RIFF native order is Little Endian
|
||||
stream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
if (stream.readInt() != WebP.RIFF_MAGIC) {
|
||||
return false;
|
||||
}
|
||||
|
||||
stream.readInt(); // Skip file size
|
||||
|
||||
if (stream.readInt() != WebP.WEBP_MAGIC) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int chunk = stream.readInt();
|
||||
|
||||
switch (chunk) {
|
||||
case WebP.CHUNK_VP8L:
|
||||
case WebP.CHUNK_VP8X:
|
||||
case WebP.CHUNK_VP8_:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
stream.setByteOrder(originalOrder);
|
||||
stream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageReader createReaderInstance(final Object extension) {
|
||||
return new WebPImageReader(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription(final Locale locale) {
|
||||
return "Google WebP File Format (WebP) Reader";
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.webp;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
|
||||
/**
|
||||
* WebPProviderInfo
|
||||
*/
|
||||
final class WebPProviderInfo extends ReaderWriterProviderInfo {
|
||||
WebPProviderInfo() {
|
||||
super(
|
||||
WebPProviderInfo.class,
|
||||
new String[] {"webp", "WEBP", "wbp", "WBP"},
|
||||
new String[] {"wbp", "webp"},
|
||||
new String[] {
|
||||
"image/webp", "image/x-webp"
|
||||
},
|
||||
"com.twelvemonkeys.imageio.plugins.webp.WebPImageReader",
|
||||
new String[] {"com.twelvemonkeys.imageio.plugins.webp.WebPImageReaderSpi"},
|
||||
null,
|
||||
null,
|
||||
false, null, null, null, null,
|
||||
true, null, null, null, null
|
||||
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.webp.lossless;
|
||||
|
||||
import com.twelvemonkeys.lang.Validate;
|
||||
|
||||
/**
|
||||
* ColorCache.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
*/
|
||||
final class ColorCache {
|
||||
private final int[] colors; // Color entries
|
||||
private final int hashShift; // Hash shift: 32 - hashBits.
|
||||
|
||||
private static final long K_HASH_MUL = 0x1e35a7bdL;
|
||||
|
||||
private static int hashPix(final int argb, final int shift) {
|
||||
return (int) (((argb * K_HASH_MUL) & 0xffffffffL) >> shift);
|
||||
}
|
||||
|
||||
ColorCache(final int hashBits) {
|
||||
Validate.isTrue(hashBits > 0, "hasBits must > 0");
|
||||
|
||||
int hashSize = 1 << hashBits;
|
||||
|
||||
colors = new int[hashSize];
|
||||
hashShift = 32 - hashBits;
|
||||
}
|
||||
|
||||
int lookup(final int key) {
|
||||
return colors[key];
|
||||
}
|
||||
|
||||
void set(final int key, final int argb) {
|
||||
colors[key] = argb;
|
||||
}
|
||||
|
||||
void insert(final int argb) {
|
||||
colors[index(argb)] = argb;
|
||||
}
|
||||
|
||||
int index(final int argb) {
|
||||
return hashPix(argb, hashShift);
|
||||
}
|
||||
|
||||
// TODO: Use boolean?
|
||||
int contains(final int argb) {
|
||||
int key = index(argb);
|
||||
return colors[key] == argb ? key : -1;
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.webp.lossless;
|
||||
|
||||
/**
|
||||
* PredictorMode.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
*/
|
||||
public interface PredictorMode {
|
||||
// Special rules:
|
||||
// Top-left pixel of image is predicted BLACK
|
||||
// Rest of top pixels is predicted L
|
||||
// Rest of leftmost pixels are predicted T
|
||||
// Rightmost pixels using TR, uses LEFTMOST pixel on SAME ROW (same distance as TR in memory!)
|
||||
|
||||
int BLACK = 0; // 0xff000000 (represents solid black color in ARGB)
|
||||
int L = 1; // L
|
||||
int T = 2; // T
|
||||
int TR = 3; // TR
|
||||
int TL = 4; // TL
|
||||
int AVG_L_TR_T = 5; // Average2(Average2(L, TR), T)
|
||||
int AVG_L_TL = 6; // Average2(L, TL)
|
||||
int AVG_L_T = 7; // Average2(L, T)
|
||||
int AVG_TL_T = 8; // Average2(TL, T)
|
||||
int AVG_T_TR = 9; // Average2(T, TR)
|
||||
int AVG_L_TL_T_TR = 10; // Average2(Average2(L, TL), Average2(T, TR))
|
||||
int SELECT = 11; // Select(L, T, TL)
|
||||
int CLAMP_ADD_SUB_FULL = 12; // ClampAddSubtractFull(L, T, TL)
|
||||
int CLAMP_ADD_SUB_HALF = 13; // ClampAddSubtractHalf(Average2(L, T), TL)
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.webp.lossless;
|
||||
|
||||
/**
|
||||
* Transform.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
*/
|
||||
final class Transform {
|
||||
final int type;
|
||||
final Object data;
|
||||
|
||||
Transform(final int type, final Object data) {
|
||||
this.type = type;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
byte[] getData() {
|
||||
return (byte[]) data;
|
||||
}
|
||||
|
||||
int[] getColorMap() {
|
||||
return (int[]) data;
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.webp.lossless;
|
||||
|
||||
/**
|
||||
* TransformType.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
*/
|
||||
// Hmm.. Why doesn't SUBTRACT_GREEN follow the convention?
|
||||
interface TransformType {
|
||||
int PREDICTOR_TRANSFORM = 0;
|
||||
int COLOR_TRANSFORM = 1;
|
||||
int SUBTRACT_GREEN = 2;
|
||||
int COLOR_INDEXING_TRANSFORM = 3;
|
||||
}
|
@ -0,0 +1,365 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.webp.lossless;
|
||||
|
||||
import com.twelvemonkeys.imageio.plugins.webp.LSBBitReader;
|
||||
|
||||
import javax.imageio.IIOException;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.awt.image.*;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.twelvemonkeys.imageio.plugins.webp.RasterUtils.asByteRaster;
|
||||
import static java.lang.Math.abs;
|
||||
|
||||
/**
|
||||
* VP8LDecoder.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
*/
|
||||
public final class VP8LDecoder {
|
||||
|
||||
private final ImageInputStream imageInput;
|
||||
private final LSBBitReader lsbBitReader;
|
||||
|
||||
private final List<Transform> transforms = new ArrayList<>();
|
||||
private ColorCache colorCache;
|
||||
|
||||
public VP8LDecoder(final ImageInputStream imageInput) {
|
||||
this.imageInput = imageInput;
|
||||
lsbBitReader = new LSBBitReader(imageInput);
|
||||
}
|
||||
|
||||
public void readVP8Lossless(final WritableRaster raster, final boolean topLevel) throws IOException {
|
||||
//https://github.com/webmproject/libwebp/blob/666bd6c65483a512fe4c2eb63fbc198b6fb4fae4/src/dec/vp8l_dec.c#L1114
|
||||
|
||||
int xSize = raster.getWidth();
|
||||
int ySize = raster.getHeight();
|
||||
|
||||
// Read transforms
|
||||
while (topLevel && lsbBitReader.readBit() == 1) {
|
||||
xSize = readTransform(xSize, ySize);
|
||||
}
|
||||
|
||||
// Read color cache size
|
||||
int colorCacheBits = 0;
|
||||
if (lsbBitReader.readBit() == 1) {
|
||||
colorCacheBits = (int) lsbBitReader.readBits(4);
|
||||
if (colorCacheBits < 1 || colorCacheBits > 11) {
|
||||
throw new IIOException("Corrupt WebP stream, colorCacheBits < 1 || > 11: " + colorCacheBits);
|
||||
}
|
||||
}
|
||||
|
||||
// Read Huffman codes
|
||||
readHuffmanCodes(colorCacheBits, topLevel);
|
||||
|
||||
if (colorCacheBits > 0) {
|
||||
colorCache = new ColorCache(colorCacheBits);
|
||||
}
|
||||
|
||||
// Use the Huffman trees to decode the LZ77 encoded data.
|
||||
// decodeImageData(raster, )
|
||||
}
|
||||
|
||||
private int readTransform(int xSize, int ySize) throws IOException {
|
||||
int transformType = (int) lsbBitReader.readBits(2);
|
||||
|
||||
// TODO: Each transform type can only be present once in the stream.
|
||||
|
||||
switch (transformType) {
|
||||
case TransformType.PREDICTOR_TRANSFORM: {
|
||||
System.err.println("transformType: PREDICTOR_TRANSFORM");
|
||||
// int sizeBits = (int) readBits(3) + 2;
|
||||
int sizeBits = (int) lsbBitReader.readBits(3) + 2;
|
||||
int size = 1 << sizeBits;
|
||||
|
||||
int blockWidth = size;
|
||||
int blockHeight = size;
|
||||
|
||||
// int blockSize = divRoundUp(width, size);
|
||||
int blockSize = divRoundUp(xSize, size);
|
||||
|
||||
for (int y = 0; y < ySize; y++) {
|
||||
for (int x = 0; x < xSize; x++) {
|
||||
int blockIndex = (y >> sizeBits) * blockSize + (x >> sizeBits);
|
||||
}
|
||||
}
|
||||
|
||||
// Special rules:
|
||||
// Top-left pixel of image is predicted BLACK
|
||||
// Rest of top pixels is predicted L
|
||||
// Rest of leftmost pixels are predicted T
|
||||
// Rightmost pixels using TR, uses LEFTMOST pixel on SAME ROW (same distance as TR in memory!)
|
||||
|
||||
// WritableRaster data = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, blockWidth, blockHeight, blockWidth, 1, new int[] {0}, null);
|
||||
// readVP8Lossless(data, false);
|
||||
//
|
||||
break;
|
||||
}
|
||||
case TransformType.COLOR_TRANSFORM: {
|
||||
// The two first transforms contains the exact same data, can be combined
|
||||
System.err.println("transformType: COLOR_TRANSFORM");
|
||||
|
||||
int sizeBits = (int) lsbBitReader.readBits(3) + 2;
|
||||
// int size = 1 << sizeBits;
|
||||
|
||||
// TODO: Understand difference between spec divRoundUp and impl VP8LSubSampleSize
|
||||
|
||||
int blockWidth = subSampleSize(xSize, sizeBits);
|
||||
int blockHeight = subSampleSize(ySize, sizeBits);
|
||||
WritableRaster data = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, blockWidth, blockHeight, blockWidth, 1, new int[] {0}, null);
|
||||
readVP8Lossless(data, false);
|
||||
|
||||
transforms.add(new Transform(transformType, ((DataBufferByte) data.getDataBuffer()).getData()));
|
||||
|
||||
break;
|
||||
}
|
||||
case TransformType.SUBTRACT_GREEN: {
|
||||
System.err.println("transformType: SUBTRACT_GREEN");
|
||||
// No data here
|
||||
|
||||
// addGreenToBlueAndRed();
|
||||
break;
|
||||
}
|
||||
case TransformType.COLOR_INDEXING_TRANSFORM: {
|
||||
System.err.println("transformType: COLOR_INDEXING_TRANSFORM");
|
||||
|
||||
// 8 bit value for color table size
|
||||
int colorTableSize = imageInput.readUnsignedByte() + 1; // 1-256
|
||||
System.err.println("colorTableSize: " + colorTableSize);
|
||||
|
||||
// If the index is equal or larger than color_table_size,
|
||||
// the argb color value should be set to 0x00000000
|
||||
// We handle this by allocating a possibly larger buffer
|
||||
int safeColorTableSize = colorTableSize > 16 ? 256 :
|
||||
colorTableSize > 4 ? 16 :
|
||||
colorTableSize > 2 ? 4 : 2;
|
||||
|
||||
System.err.println("safeColorTableSize: " + safeColorTableSize);
|
||||
|
||||
int[] colorTable = new int[safeColorTableSize];
|
||||
|
||||
// The color table can be obtained by reading an image,
|
||||
// without the RIFF header, image size, and transforms,
|
||||
// assuming a height of one pixel and a width of
|
||||
// color_table_size. The color table is always
|
||||
// subtraction-coded to reduce image entropy.
|
||||
// TODO: Read *without transforms*, using SUBTRACT_GREEN only!
|
||||
readVP8Lossless(asByteRaster(
|
||||
Raster.createPackedRaster(
|
||||
new DataBufferInt(colorTable, colorTableSize),
|
||||
colorTableSize, 1, colorTableSize,
|
||||
new int[] {0}, null
|
||||
),
|
||||
ColorModel.getRGBdefault()
|
||||
), false);
|
||||
|
||||
// TODO: We may not really need this value...
|
||||
// What we need is the number of pixels packed into each green sample (byte)
|
||||
int widthBits = colorTableSize > 16 ? 0 :
|
||||
colorTableSize > 4 ? 1 :
|
||||
colorTableSize > 2 ? 2 : 3;
|
||||
|
||||
xSize = subSampleSize(xSize, widthBits);
|
||||
|
||||
/*
|
||||
// TODO: read ARGB
|
||||
int argb = 0;
|
||||
|
||||
// Inverse transform
|
||||
// TODO: Expand to mutliple pixels?
|
||||
argb = colorTable[GREEN(argb)];
|
||||
*/
|
||||
|
||||
// TODO: Can we use this to produce an image with IndexColorModel instead of expanding the values in-memory?
|
||||
transforms.add(new Transform(transformType, colorTable));
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new AssertionError("Invalid transformType: " + transformType);
|
||||
}
|
||||
|
||||
return xSize;
|
||||
}
|
||||
|
||||
private void readHuffmanCodes(int colorCacheBits, boolean allowRecursion) {
|
||||
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
// FROM the spec
|
||||
private static int divRoundUp(final int numerator, final int denominator) {
|
||||
return (numerator + denominator - 1) / denominator;
|
||||
}
|
||||
|
||||
private static int subSampleSize(final int size, final int samplingBits) {
|
||||
return (size + (1 << samplingBits) - 1) >> samplingBits;
|
||||
}
|
||||
|
||||
private static int ALPHA(final int ARGB) {
|
||||
return ARGB >>> 24;
|
||||
}
|
||||
|
||||
private static int RED(final int ARGB) {
|
||||
return (ARGB >> 16) & 0xff;
|
||||
}
|
||||
|
||||
private static int GREEN(final int ARGB) {
|
||||
return (ARGB >> 8) & 0xff;
|
||||
}
|
||||
|
||||
private static int BLUE(final int ARGB) {
|
||||
return ARGB & 0xff;
|
||||
}
|
||||
|
||||
private static int select(final int L, final int T, final int TL) {
|
||||
// L = left pixel, T = top pixel, TL = top left pixel.
|
||||
|
||||
// ARGB component estimates for prediction.
|
||||
int pAlpha = ALPHA(L) + ALPHA(T) - ALPHA(TL);
|
||||
int pRed = RED(L) + RED(T) - RED(TL);
|
||||
int pGreen = GREEN(L) + GREEN(T) - GREEN(TL);
|
||||
int pBlue = BLUE(L) + BLUE(T) - BLUE(TL);
|
||||
|
||||
// Manhattan distances to estimates for left and top pixels.
|
||||
int pL = abs(pAlpha - ALPHA(L)) + abs(pRed - RED(L)) +
|
||||
abs(pGreen - GREEN(L)) + abs(pBlue - BLUE(L));
|
||||
int pT = abs(pAlpha - ALPHA(T)) + abs(pRed - RED(T)) +
|
||||
abs(pGreen - GREEN(T)) + abs(pBlue - BLUE(T));
|
||||
|
||||
// Return either left or top, the one closer to the prediction.
|
||||
if (pL < pT) {
|
||||
return L;
|
||||
}
|
||||
else {
|
||||
return T;
|
||||
}
|
||||
}
|
||||
|
||||
private static int average2(final int a, final int b) {
|
||||
return (a + b) / 2;
|
||||
}
|
||||
|
||||
// Clamp the input value between 0 and 255.
|
||||
private static int clamp(final int a) {
|
||||
return a < 0 ? 0 : a > 255 ? 255 : a;
|
||||
}
|
||||
|
||||
private static int clampAddSubtractFull(final int a, final int b, final int c) {
|
||||
return clamp(a + b - c);
|
||||
}
|
||||
|
||||
private static int clampAddSubtractHalf(final int a, final int b) {
|
||||
return clamp(a + (a - b) / 2);
|
||||
}
|
||||
|
||||
static final class ColorTransformElement {
|
||||
final int green_to_red;
|
||||
final int green_to_blue;
|
||||
final int red_to_blue;
|
||||
|
||||
ColorTransformElement(final int green_to_red, final int green_to_blue, final int red_to_blue) {
|
||||
this.green_to_red = green_to_red;
|
||||
this.green_to_blue = green_to_blue;
|
||||
this.red_to_blue = red_to_blue;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: For encoding!
|
||||
private static void colorTransform(final int red, final int blue, final int green,
|
||||
final ColorTransformElement trans,
|
||||
final int[] newRedBlue) {
|
||||
// Transformed values of red and blue components
|
||||
int tmp_red = red;
|
||||
int tmp_blue = blue;
|
||||
|
||||
// Applying transform is just adding the transform deltas
|
||||
tmp_red += colorTransformDelta((byte) trans.green_to_red, (byte) green);
|
||||
tmp_blue += colorTransformDelta((byte) trans.green_to_blue, (byte) green);
|
||||
tmp_blue += colorTransformDelta((byte) trans.red_to_blue, (byte) red);
|
||||
|
||||
// No pointer dereferences in Java...
|
||||
// TODO: Consider passing an offset too, so we can modify in-place
|
||||
newRedBlue[0] = tmp_red & 0xff;
|
||||
newRedBlue[1] = tmp_blue & 0xff;
|
||||
}
|
||||
|
||||
// A conversion from the 8-bit unsigned representation (uint8) to the 8-bit
|
||||
// signed one (int8) is required before calling ColorTransformDelta(). It
|
||||
// should be performed using 8-bit two's complement (that is: uint8 range
|
||||
// [128-255] is mapped to the [-128, -1] range of its converted int8
|
||||
// value).
|
||||
private static byte colorTransformDelta(final byte t, final byte c) {
|
||||
return (byte) ((t * c) >> 5);
|
||||
}
|
||||
|
||||
private static void inverseTransform(final byte red, final byte green, final byte blue,
|
||||
final ColorTransformElement trans,
|
||||
final int[] newRedBlue) {
|
||||
// Applying inverse transform is just subtracting the
|
||||
// color transform deltas
|
||||
// Transformed values of red and blue components
|
||||
int tmp_red = red;
|
||||
int tmp_blue = blue;
|
||||
|
||||
tmp_red -= colorTransformDelta((byte) trans.green_to_red, green);
|
||||
tmp_blue -= colorTransformDelta((byte) trans.green_to_blue, green);
|
||||
tmp_blue -= colorTransformDelta((byte) trans.red_to_blue, red); // Spec has red & 0xff
|
||||
|
||||
newRedBlue[0] = tmp_red & 0xff;
|
||||
newRedBlue[1] = tmp_blue & 0xff;
|
||||
}
|
||||
|
||||
private static void inverseTransform(final byte[] rgb, final ColorTransformElement trans) {
|
||||
// Applying inverse transform is just subtracting the
|
||||
// color transform deltas
|
||||
// Transformed values of red and blue components
|
||||
int tmp_red = rgb[0];
|
||||
int tmp_blue = rgb[2];
|
||||
|
||||
tmp_red -= colorTransformDelta((byte) trans.green_to_red, rgb[1]);
|
||||
tmp_blue -= colorTransformDelta((byte) trans.green_to_blue, rgb[1]);
|
||||
tmp_blue -= colorTransformDelta((byte) trans.red_to_blue, rgb[0]); // Spec has red & 0xff
|
||||
|
||||
rgb[0] = (byte) (tmp_red & 0xff);
|
||||
rgb[2] = (byte) (tmp_blue & 0xff);
|
||||
}
|
||||
|
||||
private static void addGreenToBlueAndRed(byte[] rgb) {
|
||||
rgb[0] = (byte) ((rgb[0] + rgb[1]) & 0xff);
|
||||
rgb[2] = (byte) ((rgb[2] + rgb[1]) & 0xff);
|
||||
}
|
||||
}
|
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Brooss, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.webp.vp8;
|
||||
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
final class BoolDecoder {
|
||||
private int bit_count; /* # of bits shifted out of value, at most 7 */
|
||||
ImageInputStream data;
|
||||
private long offset; /* pointer to next compressed data byte */
|
||||
private int range; /* always identical to encoder's range */
|
||||
private int value; /* contains at least 24 significant bits */
|
||||
|
||||
BoolDecoder(ImageInputStream frame, long offset) throws IOException {
|
||||
this.data = frame;
|
||||
this.offset = offset;
|
||||
initBoolDecoder();
|
||||
}
|
||||
|
||||
private void initBoolDecoder() throws IOException {
|
||||
value = 0; /* value = first 16 input bits */
|
||||
|
||||
data.seek(offset);
|
||||
value = data.readUnsignedByte() << 8;
|
||||
// value = (data[offset]) << 8;
|
||||
offset++;
|
||||
|
||||
range = 255; /* initial range is full */
|
||||
bit_count = 0; /* have not yet shifted out any bits */
|
||||
}
|
||||
|
||||
public int readBit() throws IOException {
|
||||
return readBool(128);
|
||||
}
|
||||
|
||||
public int readBool(int probability) throws IOException {
|
||||
int bit = 0;
|
||||
int split;
|
||||
int bigsplit;
|
||||
int range = this.range;
|
||||
int value = this.value;
|
||||
split = 1 + (((range - 1) * probability) >> 8);
|
||||
bigsplit = (split << 8);
|
||||
range = split;
|
||||
|
||||
if (value >= bigsplit) {
|
||||
range = this.range - split;
|
||||
value = value - bigsplit;
|
||||
bit = 1;
|
||||
}
|
||||
|
||||
{
|
||||
int count = this.bit_count;
|
||||
int shift = Globals.vp8dxBitreaderNorm[range];
|
||||
range <<= shift;
|
||||
value <<= shift;
|
||||
count -= shift;
|
||||
|
||||
if (count <= 0) {
|
||||
// data.seek(offset);
|
||||
value |= data.readUnsignedByte() << (-count);
|
||||
// value |= data[offset] << (-count);
|
||||
offset++;
|
||||
count += 8;
|
||||
}
|
||||
|
||||
this.bit_count = count;
|
||||
}
|
||||
this.value = value;
|
||||
this.range = range;
|
||||
return bit;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convenience function reads a "literal", that is, a "num_bits" wide
|
||||
* unsigned value whose bits come high- to low-order, with each bit encoded
|
||||
* at probability 128 (i.e., 1/2).
|
||||
*/
|
||||
public int readLiteral(int num_bits) throws IOException {
|
||||
int v = 0;
|
||||
while (num_bits-- > 0) {
|
||||
v = (v << 1) + readBool(128);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
// int readTree(int t[], /* tree specification */ int p[] /* corresponding interior node probabilities */) throws IOException {
|
||||
// int i = 0; /* begin at root */
|
||||
//
|
||||
// /* Descend tree until leaf is reached */
|
||||
// while ((i = t[i + readBool(p[i >> 1])]) > 0) {
|
||||
// }
|
||||
// return -i; /* return value is negation of nonpositive index */
|
||||
//
|
||||
// }
|
||||
//
|
||||
// int readTree(int t[], /* tree specification */ int p[], /* corresponding interior node probabilities */ int skip_branches) throws IOException {
|
||||
int readTree(int[] t, /* tree specification */ int[] p, /* corresponding interior node probabilities */ int skip_branches) throws IOException {
|
||||
int i = skip_branches * 2; /* begin at root */
|
||||
|
||||
/* Descend tree until leaf is reached */
|
||||
while ((i = t[i + readBool(p[i >> 1])]) > 0) {
|
||||
}
|
||||
return -i; /* return value is negation of nonpositive index */
|
||||
}
|
||||
|
||||
public void seek() throws IOException {
|
||||
data.seek(offset);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "bc: " + value;
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Brooss, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.webp.vp8;
|
||||
|
||||
final class DeltaQ {
|
||||
boolean update = false;
|
||||
int v = 0;
|
||||
}
|
@ -0,0 +1,822 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Brooss, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.webp.vp8;
|
||||
|
||||
final class Globals {
|
||||
|
||||
/* predict DC using row above and column to the left */
|
||||
static final int DC_PRED = 0;
|
||||
/* predict rows using row above */
|
||||
static final int V_PRED = 1;
|
||||
/* predict columns using column to the left */
|
||||
static final int H_PRED = 2;
|
||||
/* propagate second differences a la "true motion" */
|
||||
static final int TM_PRED = 3;
|
||||
/* each Y subblock is independently predicted */
|
||||
static final int B_PRED = 4;
|
||||
|
||||
static String getModeAsString(int mode) {
|
||||
switch (mode) {
|
||||
case DC_PRED:
|
||||
return "DC_PRED";
|
||||
case V_PRED:
|
||||
return "V_PRED";
|
||||
case H_PRED:
|
||||
return "H_PRED";
|
||||
case TM_PRED:
|
||||
return "TM_PRED";
|
||||
case B_PRED:
|
||||
return "B_PRED";
|
||||
}
|
||||
return "not found";
|
||||
}
|
||||
|
||||
/* intra_bmode */
|
||||
static final int B_DC_PRED = 0; /*
|
||||
* predict DC using row above and
|
||||
* column to the left
|
||||
*/
|
||||
static final int B_TM_PRED = 1; /*
|
||||
* propagate second differences a la
|
||||
* "true motion"
|
||||
*/
|
||||
static final int B_VE_PRED = 2; /* predict rows using row above */
|
||||
static final int B_HE_PRED = 3; /*
|
||||
* predict columns using column to
|
||||
* the left
|
||||
*/
|
||||
static final int B_LD_PRED = 4; /*
|
||||
* southwest (left and down) 45
|
||||
* degree diagonal prediction
|
||||
*/
|
||||
static final int B_RD_PRED = 5; /* southeast (right and down) "" */
|
||||
static final int B_VR_PRED = 6; /*
|
||||
* SSE (vertical right) diagonal
|
||||
* prediction
|
||||
*/
|
||||
static final int B_VL_PRED = 7; /* SSW (vertical left) "" */
|
||||
static final int B_HD_PRED = 8; /* ESE (horizontal down) "" */
|
||||
static final int B_HU_PRED = 9; /* ENE (horizontal up) "" */
|
||||
|
||||
static String getSubBlockModeAsString(int mode) {
|
||||
switch (mode) {
|
||||
case B_DC_PRED:
|
||||
return "B_DC_PRED";
|
||||
case B_TM_PRED:
|
||||
return "B_TM_PRED";
|
||||
case B_VE_PRED:
|
||||
return "B_VE_PRED";
|
||||
case B_HE_PRED:
|
||||
return "B_HE_PRED";
|
||||
case B_LD_PRED:
|
||||
return "B_LD_PRED";
|
||||
case B_RD_PRED:
|
||||
return "B_RD_PRED";
|
||||
case B_VR_PRED:
|
||||
return "B_VR_PRED";
|
||||
case B_VL_PRED:
|
||||
return "B_VL_PRED";
|
||||
case B_HD_PRED:
|
||||
return "B_HD_PRED";
|
||||
case B_HU_PRED:
|
||||
return "B_HU_PRED";
|
||||
}
|
||||
return "not found";
|
||||
}
|
||||
|
||||
static final int MAX_MB_SEGMENTS = 4;
|
||||
static final int MB_LVL_MAX = 2;
|
||||
static int[] vp8MacroBlockFeatureDataBits = { 7, 6 };
|
||||
static final int MB_FEATURE_TREE_PROBS = 3;
|
||||
|
||||
static int macroBlockSegmentTree[] = { 2, 4,
|
||||
/* root: "0", "1" subtrees */
|
||||
-0, -1,
|
||||
/* "00" = 0th value, "01" = 1st value */
|
||||
-2, -3
|
||||
/* "10" = 2nd value, "11" = 3rd value */
|
||||
};
|
||||
|
||||
static int vp8KeyFrameYModeTree[] = { -B_PRED, 2, 4, 6, -DC_PRED,
|
||||
-V_PRED, -H_PRED, -TM_PRED };
|
||||
|
||||
static int vp8SubBlockModeTree[] = { -B_DC_PRED, 2, /*
|
||||
* B_DC_PRED =
|
||||
* "0"
|
||||
*/
|
||||
-B_TM_PRED, 4, /* B_TM_PRED = "10" */
|
||||
-B_VE_PRED, 6, /* B_VE_PRED = "110" */
|
||||
8, 12, -B_HE_PRED, 10, /* B_HE_PRED = "1110" */
|
||||
-B_RD_PRED, -B_VR_PRED, /* B_RD_PRED = "111100", B_VR_PRED = "111101" */
|
||||
-B_LD_PRED, 14, /* B_LD_PRED = "111110" */
|
||||
-B_VL_PRED, 16, /* B_VL_PRED = "1111110" */
|
||||
-B_HD_PRED, -B_HU_PRED /* HD = "11111110", HU = "11111111" */
|
||||
};
|
||||
|
||||
static int[] vp8KeyFrameYModeProb = {145, 156, 163, 128 };
|
||||
|
||||
// uv
|
||||
static int[] vp8UVModeTree = {-DC_PRED, 2, /*
|
||||
* root: DC_PRED = "0",
|
||||
* "1" subtree
|
||||
*/
|
||||
-V_PRED, 4, /* "1" subtree: V_PRED = "10", "11" subtree */
|
||||
-H_PRED, -TM_PRED /* "11" subtree: H_PRED = "110", TM_PRED = "111" */
|
||||
};
|
||||
static int[] vp8KeyFrameUVModeProb = {142, 114, 183 };
|
||||
|
||||
static int[][][] vp8KeyFrameSubBlockModeProb = {
|
||||
{ { 231, 120, 48, 89, 115, 113, 120, 152, 112 },
|
||||
{ 152, 179, 64, 126, 170, 118, 46, 70, 95 },
|
||||
{ 175, 69, 143, 80, 85, 82, 72, 155, 103 },
|
||||
{ 56, 58, 10, 171, 218, 189, 17, 13, 152 },
|
||||
{ 144, 71, 10, 38, 171, 213, 144, 34, 26 },
|
||||
{ 114, 26, 17, 163, 44, 195, 21, 10, 173 },
|
||||
{ 121, 24, 80, 195, 26, 62, 44, 64, 85 },
|
||||
{ 170, 46, 55, 19, 136, 160, 33, 206, 71 },
|
||||
{ 63, 20, 8, 114, 114, 208, 12, 9, 226 },
|
||||
{ 81, 40, 11, 96, 182, 84, 29, 16, 36 } },
|
||||
{ { 134, 183, 89, 137, 98, 101, 106, 165, 148 },
|
||||
{ 72, 187, 100, 130, 157, 111, 32, 75, 80 },
|
||||
{ 66, 102, 167, 99, 74, 62, 40, 234, 128 },
|
||||
{ 41, 53, 9, 178, 241, 141, 26, 8, 107 },
|
||||
{ 104, 79, 12, 27, 217, 255, 87, 17, 7 },
|
||||
{ 74, 43, 26, 146, 73, 166, 49, 23, 157 },
|
||||
{ 65, 38, 105, 160, 51, 52, 31, 115, 128 },
|
||||
{ 87, 68, 71, 44, 114, 51, 15, 186, 23 },
|
||||
{ 47, 41, 14, 110, 182, 183, 21, 17, 194 },
|
||||
{ 66, 45, 25, 102, 197, 189, 23, 18, 22 } },
|
||||
{ { 88, 88, 147, 150, 42, 46, 45, 196, 205 },
|
||||
{ 43, 97, 183, 117, 85, 38, 35, 179, 61 },
|
||||
{ 39, 53, 200, 87, 26, 21, 43, 232, 171 },
|
||||
{ 56, 34, 51, 104, 114, 102, 29, 93, 77 },
|
||||
{ 107, 54, 32, 26, 51, 1, 81, 43, 31 },
|
||||
{ 39, 28, 85, 171, 58, 165, 90, 98, 64 },
|
||||
{ 34, 22, 116, 206, 23, 34, 43, 166, 73 },
|
||||
{ 68, 25, 106, 22, 64, 171, 36, 225, 114 },
|
||||
{ 34, 19, 21, 102, 132, 188, 16, 76, 124 },
|
||||
{ 62, 18, 78, 95, 85, 57, 50, 48, 51 } },
|
||||
{ { 193, 101, 35, 159, 215, 111, 89, 46, 111 },
|
||||
{ 60, 148, 31, 172, 219, 228, 21, 18, 111 },
|
||||
{ 112, 113, 77, 85, 179, 255, 38, 120, 114 },
|
||||
{ 40, 42, 1, 196, 245, 209, 10, 25, 109 },
|
||||
{ 100, 80, 8, 43, 154, 1, 51, 26, 71 },
|
||||
{ 88, 43, 29, 140, 166, 213, 37, 43, 154 },
|
||||
{ 61, 63, 30, 155, 67, 45, 68, 1, 209 },
|
||||
{ 142, 78, 78, 16, 255, 128, 34, 197, 171 },
|
||||
{ 41, 40, 5, 102, 211, 183, 4, 1, 221 },
|
||||
{ 51, 50, 17, 168, 209, 192, 23, 25, 82 } },
|
||||
{ { 125, 98, 42, 88, 104, 85, 117, 175, 82 },
|
||||
{ 95, 84, 53, 89, 128, 100, 113, 101, 45 },
|
||||
{ 75, 79, 123, 47, 51, 128, 81, 171, 1 },
|
||||
{ 57, 17, 5, 71, 102, 57, 53, 41, 49 },
|
||||
{ 115, 21, 2, 10, 102, 255, 166, 23, 6 },
|
||||
{ 38, 33, 13, 121, 57, 73, 26, 1, 85 },
|
||||
{ 41, 10, 67, 138, 77, 110, 90, 47, 114 },
|
||||
{ 101, 29, 16, 10, 85, 128, 101, 196, 26 },
|
||||
{ 57, 18, 10, 102, 102, 213, 34, 20, 43 },
|
||||
{ 117, 20, 15, 36, 163, 128, 68, 1, 26 } },
|
||||
{ { 138, 31, 36, 171, 27, 166, 38, 44, 229 },
|
||||
{ 67, 87, 58, 169, 82, 115, 26, 59, 179 },
|
||||
{ 63, 59, 90, 180, 59, 166, 93, 73, 154 },
|
||||
{ 40, 40, 21, 116, 143, 209, 34, 39, 175 },
|
||||
{ 57, 46, 22, 24, 128, 1, 54, 17, 37 },
|
||||
{ 47, 15, 16, 183, 34, 223, 49, 45, 183 },
|
||||
{ 46, 17, 33, 183, 6, 98, 15, 32, 183 },
|
||||
{ 65, 32, 73, 115, 28, 128, 23, 128, 205 },
|
||||
{ 40, 3, 9, 115, 51, 192, 18, 6, 223 },
|
||||
{ 87, 37, 9, 115, 59, 77, 64, 21, 47 } },
|
||||
{ { 104, 55, 44, 218, 9, 54, 53, 130, 226 },
|
||||
{ 64, 90, 70, 205, 40, 41, 23, 26, 57 },
|
||||
{ 54, 57, 112, 184, 5, 41, 38, 166, 213 },
|
||||
{ 30, 34, 26, 133, 152, 116, 10, 32, 134 },
|
||||
{ 75, 32, 12, 51, 192, 255, 160, 43, 51 },
|
||||
{ 39, 19, 53, 221, 26, 114, 32, 73, 255 },
|
||||
{ 31, 9, 65, 234, 2, 15, 1, 118, 73 },
|
||||
{ 88, 31, 35, 67, 102, 85, 55, 186, 85 },
|
||||
{ 56, 21, 23, 111, 59, 205, 45, 37, 192 },
|
||||
{ 55, 38, 70, 124, 73, 102, 1, 34, 98 } },
|
||||
{ { 102, 61, 71, 37, 34, 53, 31, 243, 192 },
|
||||
{ 69, 60, 71, 38, 73, 119, 28, 222, 37 },
|
||||
{ 68, 45, 128, 34, 1, 47, 11, 245, 171 },
|
||||
{ 62, 17, 19, 70, 146, 85, 55, 62, 70 },
|
||||
{ 75, 15, 9, 9, 64, 255, 184, 119, 16 },
|
||||
{ 37, 43, 37, 154, 100, 163, 85, 160, 1 },
|
||||
{ 63, 9, 92, 136, 28, 64, 32, 201, 85 },
|
||||
{ 86, 6, 28, 5, 64, 255, 25, 248, 1 },
|
||||
{ 56, 8, 17, 132, 137, 255, 55, 116, 128 },
|
||||
{ 58, 15, 20, 82, 135, 57, 26, 121, 40 } },
|
||||
{ { 164, 50, 31, 137, 154, 133, 25, 35, 218 },
|
||||
{ 51, 103, 44, 131, 131, 123, 31, 6, 158 },
|
||||
{ 86, 40, 64, 135, 148, 224, 45, 183, 128 },
|
||||
{ 22, 26, 17, 131, 240, 154, 14, 1, 209 },
|
||||
{ 83, 12, 13, 54, 192, 255, 68, 47, 28 },
|
||||
{ 45, 16, 21, 91, 64, 222, 7, 1, 197 },
|
||||
{ 56, 21, 39, 155, 60, 138, 23, 102, 213 },
|
||||
{ 85, 26, 85, 85, 128, 128, 32, 146, 171 },
|
||||
{ 18, 11, 7, 63, 144, 171, 4, 4, 246 },
|
||||
{ 35, 27, 10, 146, 174, 171, 12, 26, 128 } },
|
||||
{ { 190, 80, 35, 99, 180, 80, 126, 54, 45 },
|
||||
{ 85, 126, 47, 87, 176, 51, 41, 20, 32 },
|
||||
{ 101, 75, 128, 139, 118, 146, 116, 128, 85 },
|
||||
{ 56, 41, 15, 176, 236, 85, 37, 9, 62 },
|
||||
{ 146, 36, 19, 30, 171, 255, 97, 27, 20 },
|
||||
{ 71, 30, 17, 119, 118, 255, 17, 18, 138 },
|
||||
{ 101, 38, 60, 138, 55, 70, 43, 26, 142 },
|
||||
{ 138, 45, 61, 62, 219, 1, 81, 188, 64 },
|
||||
{ 32, 41, 20, 117, 151, 142, 20, 21, 163 },
|
||||
{ 112, 19, 12, 61, 195, 128, 48, 4, 24 } } };
|
||||
|
||||
static int[][][][] vp8CoefUpdateProbs = new int[][][][] {
|
||||
{
|
||||
{
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
{ 176, 246, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 223, 241, 252, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 249, 253, 253, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
{ 255, 244, 252, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 234, 254, 254, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 253, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
{ 255, 246, 254, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 239, 253, 254, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 254, 255, 254, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
{ 255, 248, 254, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 251, 255, 254, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
{ 255, 253, 254, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 251, 254, 254, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 254, 255, 254, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
{ 255, 254, 253, 255, 254, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 250, 255, 254, 255, 254, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
|
||||
{ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 } } },
|
||||
{
|
||||
{
|
||||
{ 217, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 225, 252, 241, 253, 255, 255, 254, 255, 255, 255,
|
||||
255 },
|
||||
{ 234, 250, 241, 250, 253, 255, 253, 254, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
{ 255, 254, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 223, 254, 254, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 238, 253, 254, 254, 255, 255, 255, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
{ 255, 248, 254, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 249, 254, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
{ 255, 253, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 247, 254, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
{ 255, 253, 254, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 252, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
{ 255, 254, 254, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 253, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
{ 255, 254, 253, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 250, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 } } },
|
||||
{
|
||||
{
|
||||
{ 186, 251, 250, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 234, 251, 244, 254, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 251, 251, 243, 253, 254, 255, 254, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
|
||||
{ 255, 253, 254, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 236, 253, 254, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 251, 253, 253, 254, 254, 255, 255, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
{ 255, 254, 254, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 254, 254, 254, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
{ 255, 254, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 254, 254, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 } } },
|
||||
{
|
||||
{
|
||||
{ 248, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 250, 254, 252, 254, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 248, 254, 249, 253, 255, 255, 255, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
{ 255, 253, 253, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 246, 253, 253, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 252, 254, 251, 254, 254, 255, 255, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
{ 255, 254, 252, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 248, 254, 253, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 253, 255, 254, 254, 255, 255, 255, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
{ 255, 251, 254, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 245, 251, 254, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 253, 253, 254, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
|
||||
{ 255, 251, 253, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 252, 253, 254, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 255, 254, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
{ 255, 252, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 249, 255, 254, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 255, 255, 254, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
{ 255, 255, 253, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 250, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 } },
|
||||
{
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255 } } } };
|
||||
|
||||
static int[][][][] getDefaultCoefProbs() {
|
||||
int[][][][] r = new int[vp8DefaultCoefProbs.length][vp8DefaultCoefProbs[0].length][vp8DefaultCoefProbs[0][0].length][vp8DefaultCoefProbs[0][0][0].length];
|
||||
for (int i = 0; i < vp8DefaultCoefProbs.length; i++)
|
||||
for (int j = 0; j < vp8DefaultCoefProbs[0].length; j++)
|
||||
for (int k = 0; k < vp8DefaultCoefProbs[0][0].length; k++)
|
||||
for (int l = 0; l < vp8DefaultCoefProbs[0][0][0].length; l++)
|
||||
r[i][j][k][l] = vp8DefaultCoefProbs[i][j][k][l];
|
||||
return r;
|
||||
}
|
||||
|
||||
private static final int[][][][] vp8DefaultCoefProbs = new int[][][][] {
|
||||
{
|
||||
{
|
||||
{ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
|
||||
128 } },
|
||||
{
|
||||
{ 253, 136, 254, 255, 228, 219, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 189, 129, 242, 255, 227, 213, 255, 219, 128, 128,
|
||||
128 },
|
||||
{ 106, 126, 227, 252, 214, 209, 255, 255, 128, 128,
|
||||
128 } },
|
||||
{
|
||||
{ 1, 98, 248, 255, 236, 226, 255, 255, 128, 128,
|
||||
128 },
|
||||
{ 181, 133, 238, 254, 221, 234, 255, 154, 128, 128,
|
||||
128 },
|
||||
{ 78, 134, 202, 247, 198, 180, 255, 219, 128, 128,
|
||||
128 } },
|
||||
{
|
||||
{ 1, 185, 249, 255, 243, 255, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 184, 150, 247, 255, 236, 224, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 77, 110, 216, 255, 236, 230, 128, 128, 128, 128,
|
||||
128 } },
|
||||
{
|
||||
{ 1, 101, 251, 255, 241, 255, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 170, 139, 241, 252, 236, 209, 255, 255, 128, 128,
|
||||
128 },
|
||||
{ 37, 116, 196, 243, 228, 255, 255, 255, 128, 128,
|
||||
128 } },
|
||||
{
|
||||
|
||||
{ 1, 204, 254, 255, 245, 255, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 207, 160, 250, 255, 238, 128, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 102, 103, 231, 255, 211, 171, 128, 128, 128, 128,
|
||||
128 } },
|
||||
{
|
||||
{ 1, 152, 252, 255, 240, 255, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 177, 135, 243, 255, 234, 225, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 80, 129, 211, 255, 194, 224, 128, 128, 128, 128,
|
||||
128 } },
|
||||
{
|
||||
{ 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 246, 1, 255, 128, 128, 128, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 255, 128, 128, 128, 128, 128, 128, 128, 128, 128,
|
||||
128 } } },
|
||||
{
|
||||
{
|
||||
{ 198, 35, 237, 223, 193, 187, 162, 160, 145, 155,
|
||||
62 },
|
||||
{ 131, 45, 198, 221, 172, 176, 220, 157, 252, 221,
|
||||
1 },
|
||||
{ 68, 47, 146, 208, 149, 167, 221, 162, 255, 223,
|
||||
128 } },
|
||||
{
|
||||
{ 1, 149, 241, 255, 221, 224, 255, 255, 128, 128,
|
||||
128 },
|
||||
{ 184, 141, 234, 253, 222, 220, 255, 199, 128, 128,
|
||||
128 },
|
||||
{ 81, 99, 181, 242, 176, 190, 249, 202, 255, 255,
|
||||
128 } },
|
||||
{
|
||||
{ 1, 129, 232, 253, 214, 197, 242, 196, 255, 255,
|
||||
128 },
|
||||
{ 99, 121, 210, 250, 201, 198, 255, 202, 128, 128,
|
||||
128 },
|
||||
{ 23, 91, 163, 242, 170, 187, 247, 210, 255, 255,
|
||||
128 } },
|
||||
{
|
||||
{ 1, 200, 246, 255, 234, 255, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 109, 178, 241, 255, 231, 245, 255, 255, 128, 128,
|
||||
128 },
|
||||
{ 44, 130, 201, 253, 205, 192, 255, 255, 128, 128,
|
||||
128 } },
|
||||
{
|
||||
{ 1, 132, 239, 251, 219, 209, 255, 165, 128, 128,
|
||||
128 },
|
||||
{ 94, 136, 225, 251, 218, 190, 255, 255, 128, 128,
|
||||
128 },
|
||||
{ 22, 100, 174, 245, 186, 161, 255, 199, 128, 128,
|
||||
128 } },
|
||||
{
|
||||
{ 1, 182, 249, 255, 232, 235, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 124, 143, 241, 255, 227, 234, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 35, 77, 181, 251, 193, 211, 255, 205, 128, 128,
|
||||
128 } },
|
||||
{
|
||||
{ 1, 157, 247, 255, 236, 231, 255, 255, 128, 128,
|
||||
128 },
|
||||
{ 121, 141, 235, 255, 225, 227, 255, 255, 128, 128,
|
||||
128 },
|
||||
{ 45, 99, 188, 251, 195, 217, 255, 224, 128, 128,
|
||||
128 } },
|
||||
{
|
||||
{ 1, 1, 251, 255, 213, 255, 128, 128, 128, 128, 128 },
|
||||
{ 203, 1, 248, 255, 255, 128, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 137, 1, 177, 255, 224, 255, 128, 128, 128, 128,
|
||||
128 } } },
|
||||
|
||||
{
|
||||
{
|
||||
{ 253, 9, 248, 251, 207, 208, 255, 192, 128, 128,
|
||||
128 },
|
||||
{ 175, 13, 224, 243, 193, 185, 249, 198, 255, 255,
|
||||
128 },
|
||||
{ 73, 17, 171, 221, 161, 179, 236, 167, 255, 234,
|
||||
128 } },
|
||||
{
|
||||
{ 1, 95, 247, 253, 212, 183, 255, 255, 128, 128,
|
||||
128 },
|
||||
{ 239, 90, 244, 250, 211, 209, 255, 255, 128, 128,
|
||||
128 },
|
||||
{ 155, 77, 195, 248, 188, 195, 255, 255, 128, 128,
|
||||
128 } },
|
||||
{
|
||||
{ 1, 24, 239, 251, 218, 219, 255, 205, 128, 128,
|
||||
128 },
|
||||
{ 201, 51, 219, 255, 196, 186, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 69, 46, 190, 239, 201, 218, 255, 228, 128, 128,
|
||||
128 } },
|
||||
{
|
||||
{ 1, 191, 251, 255, 255, 128, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 223, 165, 249, 255, 213, 255, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 141, 124, 248, 255, 255, 128, 128, 128, 128, 128,
|
||||
128 } },
|
||||
{
|
||||
{ 1, 16, 248, 255, 255, 128, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 190, 36, 230, 255, 236, 255, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 149, 1, 255, 128, 128, 128, 128, 128, 128, 128,
|
||||
128 } },
|
||||
{
|
||||
{ 1, 226, 255, 128, 128, 128, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 247, 192, 255, 128, 128, 128, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 240, 128, 255, 128, 128, 128, 128, 128, 128, 128,
|
||||
128 } },
|
||||
{
|
||||
{ 1, 134, 252, 255, 255, 128, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 213, 62, 250, 255, 255, 128, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 55, 93, 255, 128, 128, 128, 128, 128, 128, 128,
|
||||
128 } },
|
||||
{
|
||||
{ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
|
||||
128 } } },
|
||||
{
|
||||
{
|
||||
{ 202, 24, 213, 235, 186, 191, 220, 160, 240, 175,
|
||||
255 },
|
||||
{ 126, 38, 182, 232, 169, 184, 228, 174, 255, 187,
|
||||
128 },
|
||||
{ 61, 46, 138, 219, 151, 178, 240, 170, 255, 216,
|
||||
128 } },
|
||||
{
|
||||
{ 1, 112, 230, 250, 199, 191, 247, 159, 255, 255,
|
||||
128 },
|
||||
{ 166, 109, 228, 252, 211, 215, 255, 174, 128, 128,
|
||||
128 },
|
||||
{ 39, 77, 162, 232, 172, 180, 245, 178, 255, 255,
|
||||
128 } },
|
||||
{
|
||||
{ 1, 52, 220, 246, 198, 199, 249, 220, 255, 255,
|
||||
128 },
|
||||
{ 124, 74, 191, 243, 183, 193, 250, 221, 255, 255,
|
||||
128 },
|
||||
{ 24, 71, 130, 219, 154, 170, 243, 182, 255, 255,
|
||||
128 }
|
||||
|
||||
},
|
||||
{
|
||||
{ 1, 182, 225, 249, 219, 240, 255, 224, 128, 128,
|
||||
128 },
|
||||
{ 149, 150, 226, 252, 216, 205, 255, 171, 128, 128,
|
||||
128 },
|
||||
{ 28, 108, 170, 242, 183, 194, 254, 223, 255, 255,
|
||||
128 } },
|
||||
{
|
||||
{ 1, 81, 230, 252, 204, 203, 255, 192, 128, 128,
|
||||
128 },
|
||||
{ 123, 102, 209, 247, 188, 196, 255, 233, 128, 128,
|
||||
128 },
|
||||
{ 20, 95, 153, 243, 164, 173, 255, 203, 128, 128,
|
||||
128 } },
|
||||
{
|
||||
{ 1, 222, 248, 255, 216, 213, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 168, 175, 246, 252, 235, 205, 255, 255, 128, 128,
|
||||
128 },
|
||||
{ 47, 116, 215, 255, 211, 212, 255, 255, 128, 128,
|
||||
128 } },
|
||||
{
|
||||
{ 1, 121, 236, 253, 212, 214, 255, 255, 128, 128,
|
||||
128 },
|
||||
{ 141, 84, 213, 252, 201, 202, 255, 219, 128, 128,
|
||||
128 },
|
||||
{ 42, 80, 160, 240, 162, 185, 255, 205, 128, 128,
|
||||
128 } },
|
||||
{
|
||||
{ 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 244, 1, 255, 128, 128, 128, 128, 128, 128, 128,
|
||||
128 },
|
||||
{ 238, 1, 255, 128, 128, 128, 128, 128, 128, 128,
|
||||
128 } } } };
|
||||
|
||||
static final int DCT_0 = 0; /* value 0 */
|
||||
static final int DCT_1 = 1; /* 1 */
|
||||
static final int DCT_2 = 2; /* 2 */
|
||||
static final int DCT_3 = 3; /* 3 */
|
||||
static final int DCT_4 = 4; /* 4 */
|
||||
static final int dct_cat1 = 5; /* range 5 - 6 (size 2) */
|
||||
static final int dct_cat2 = 6; /* 7 - 10 (4) */
|
||||
static final int dct_cat3 = 7; /* 11 - 18 (8) */
|
||||
static final int dct_cat4 = 8; /* 19 - 34 (16) */
|
||||
static final int dct_cat5 = 9; /* 35 - 66 (32) */
|
||||
static final int dct_cat6 = 10; /* 67 - 2048 (1982) */
|
||||
static final int dct_eob = 11; /* end of block */
|
||||
|
||||
static final int[] vp8CoefTree = {-dct_eob, 2, /* eob = "0" */
|
||||
-DCT_0, 4, /* 0 = "10" */
|
||||
-DCT_1, 6, /* 1 = "110" */
|
||||
8, 12, -DCT_2, 10, /* 2 = "11100" */
|
||||
-DCT_3, -DCT_4, /* 3 = "111010", 4 = "111011" */
|
||||
14, 16, -dct_cat1, -dct_cat2, /* cat1 = "111100", cat2 = "111101" */
|
||||
18, 20, -dct_cat3, -dct_cat4, /* cat3 = "1111100", cat4 = "1111101" */
|
||||
-dct_cat5, -dct_cat6 /* cat4 = "1111110", cat4 = "1111111" */
|
||||
};
|
||||
|
||||
static final int[] vp8CoefTreeNoEOB = {
|
||||
// -dct_eob, 2, /* eob = "0" */
|
||||
-DCT_0, 4, /* 0 = "10" */
|
||||
-DCT_1, 6, /* 1 = "110" */
|
||||
8, 12, -DCT_2, 10, /* 2 = "11100" */
|
||||
-DCT_3, -DCT_4, /* 3 = "111010", 4 = "111011" */
|
||||
14, 16, -dct_cat1, -dct_cat2, /* cat1 = "111100", cat2 = "111101" */
|
||||
18, 20, -dct_cat3, -dct_cat4, /* cat3 = "1111100", cat4 = "1111101" */
|
||||
-dct_cat5, -dct_cat6 /* cat4 = "1111110", cat4 = "1111111" */
|
||||
};
|
||||
|
||||
final static int[] Pcat1 = {159, 0 };
|
||||
final static int[] Pcat2 = {165, 145, 0 };
|
||||
final static int[] Pcat3 = {173, 148, 140, 0 };
|
||||
final static int[] Pcat4 = {176, 155, 140, 135, 0 };
|
||||
final static int[] Pcat5 = {180, 157, 141, 134, 130, 0 };
|
||||
final static int[] Pcat6 = {254, 254, 243, 230, 196, 177, 153, 140,
|
||||
133, 130, 129, 0 };
|
||||
static final int[] vp8CoefBands = {0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6,
|
||||
6, 6, 6, 6, 7 };
|
||||
static final int[] vp8defaultZigZag1d = {0, 1, 4, 8, 5, 2, 3, 6, 9,
|
||||
12, 13, 10, 7, 11, 14, 15,};
|
||||
|
||||
static final int[] vp8dxBitreaderNorm = {0, 7, 6, 6, 5, 5, 5, 5, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0 };
|
||||
|
||||
static final int[] vp8DcQLookup = {4, 5, 6, 7, 8, 9, 10, 10, 11,
|
||||
12, 13, 14, 15, 16, 17, 17, 18, 19, 20, 20, 21, 21, 22, 22, 23, 23,
|
||||
24, 25, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 37, 38,
|
||||
39, 40, 41, 42, 43, 44, 45, 46, 46, 47, 48, 49, 50, 51, 52, 53, 54,
|
||||
55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
|
||||
72, 73, 74, 75, 76, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87,
|
||||
88, 89, 91, 93, 95, 96, 98, 100, 101, 102, 104, 106, 108, 110, 112,
|
||||
114, 116, 118, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140,
|
||||
143, 145, 148, 151, 154, 157,};
|
||||
|
||||
static final int[] vp8AcQLookup = {4, 5, 6, 7, 8, 9, 10, 11, 12,
|
||||
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
|
||||
30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
|
||||
47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 60, 62, 64, 66, 68,
|
||||
70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100,
|
||||
102, 104, 106, 108, 110, 112, 114, 116, 119, 122, 125, 128, 131,
|
||||
134, 137, 140, 143, 146, 149, 152, 155, 158, 161, 164, 167, 170,
|
||||
173, 177, 181, 185, 189, 193, 197, 201, 205, 209, 213, 217, 221,
|
||||
225, 229, 234, 239, 245, 249, 254, 259, 264, 269, 274, 279, 284,};
|
||||
|
||||
static String toHex(int c) {
|
||||
return String.format("%1$#x ", c);
|
||||
}
|
||||
|
||||
// clamp between 0 and value
|
||||
static int clamp(final int input, final int value) {
|
||||
return input < 0 ? 0 : input > value ? value : input;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Brooss, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.webp.vp8;
|
||||
|
||||
final class IDCT {
|
||||
/* IDCT implementation */
|
||||
private static final int cospi8sqrt2minus1 = 20091;
|
||||
|
||||
private static final int sinpi8sqrt2 = 35468;
|
||||
|
||||
public static int[][] idct4x4llm(int input[]) {
|
||||
int i;
|
||||
int a1, b1, c1, d1;
|
||||
int offset = 0;
|
||||
|
||||
int[] output = new int[16];
|
||||
int temp1, temp2;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
a1 = input[offset + 0] + input[offset + 8];
|
||||
b1 = input[offset + 0] - input[offset + 8];
|
||||
|
||||
temp1 = (input[offset + 4] * sinpi8sqrt2) >> 16;
|
||||
temp2 = input[offset + 12]
|
||||
+ ((input[offset + 12] * cospi8sqrt2minus1) >> 16);
|
||||
|
||||
c1 = temp1 - temp2;
|
||||
|
||||
temp1 = input[offset + 4]
|
||||
+ ((input[offset + 4] * cospi8sqrt2minus1) >> 16);
|
||||
temp2 = (input[offset + 12] * sinpi8sqrt2) >> 16;
|
||||
d1 = temp1 + temp2;
|
||||
|
||||
output[offset + (0 * 4)] = a1 + d1;
|
||||
output[offset + (3 * 4)] = a1 - d1;
|
||||
output[offset + (1 * 4)] = b1 + c1;
|
||||
output[offset + (2 * 4)] = b1 - c1;
|
||||
|
||||
offset++;
|
||||
}
|
||||
|
||||
int diffo = 0;
|
||||
int[][] diff = new int[4][4];
|
||||
offset = 0;
|
||||
for (i = 0; i < 4; i++) {
|
||||
a1 = output[(offset * 4) + 0] + output[(offset * 4) + 2];
|
||||
b1 = output[(offset * 4) + 0] - output[(offset * 4) + 2];
|
||||
|
||||
temp1 = (output[(offset * 4) + 1] * sinpi8sqrt2) >> 16;
|
||||
temp2 = output[(offset * 4) + 3]
|
||||
+ ((output[(offset * 4) + 3] * cospi8sqrt2minus1) >> 16);
|
||||
c1 = temp1 - temp2;
|
||||
|
||||
temp1 = output[(offset * 4) + 1]
|
||||
+ ((output[(offset * 4) + 1] * cospi8sqrt2minus1) >> 16);
|
||||
temp2 = (output[(offset * 4) + 3] * sinpi8sqrt2) >> 16;
|
||||
d1 = temp1 + temp2;
|
||||
|
||||
output[(offset * 4) + 0] = (a1 + d1 + 4) >> 3;
|
||||
output[(offset * 4) + 3] = (a1 - d1 + 4) >> 3;
|
||||
output[(offset * 4) + 1] = (b1 + c1 + 4) >> 3;
|
||||
output[(offset * 4) + 2] = (b1 - c1 + 4) >> 3;
|
||||
|
||||
diff[0][diffo] = (a1 + d1 + 4) >> 3;
|
||||
diff[3][diffo] = (a1 - d1 + 4) >> 3;
|
||||
diff[1][diffo] = (b1 + c1 + 4) >> 3;
|
||||
diff[2][diffo] = (b1 - c1 + 4) >> 3;
|
||||
|
||||
offset++;
|
||||
diffo++;
|
||||
}
|
||||
|
||||
return diff;
|
||||
|
||||
}
|
||||
|
||||
public static int[][] iwalsh4x4(int[] input) {
|
||||
int i;
|
||||
int a1, b1, c1, d1;
|
||||
int a2, b2, c2, d2;
|
||||
|
||||
int[] output = new int[16];
|
||||
int[][] diff = new int[4][4];
|
||||
int offset = 0;
|
||||
for (i = 0; i < 4; i++) {
|
||||
a1 = input[offset + 0] + input[offset + 12];
|
||||
b1 = input[offset + 4] + input[offset + 8];
|
||||
c1 = input[offset + 4] - input[offset + 8];
|
||||
d1 = input[offset + 0] - input[offset + 12];
|
||||
|
||||
output[offset + 0] = a1 + b1;
|
||||
output[offset + 4] = c1 + d1;
|
||||
output[offset + 8] = a1 - b1;
|
||||
output[offset + 12] = d1 - c1;
|
||||
offset++;
|
||||
}
|
||||
|
||||
offset = 0;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
a1 = output[offset + 0] + output[offset + 3];
|
||||
b1 = output[offset + 1] + output[offset + 2];
|
||||
c1 = output[offset + 1] - output[offset + 2];
|
||||
d1 = output[offset + 0] - output[offset + 3];
|
||||
|
||||
a2 = a1 + b1;
|
||||
b2 = c1 + d1;
|
||||
c2 = a1 - b1;
|
||||
d2 = d1 - c1;
|
||||
output[offset + 0] = (a2 + 3) >> 3;
|
||||
output[offset + 1] = (b2 + 3) >> 3;
|
||||
output[offset + 2] = (c2 + 3) >> 3;
|
||||
output[offset + 3] = (d2 + 3) >> 3;
|
||||
diff[0][i] = (a2 + 3) >> 3;
|
||||
diff[1][i] = (b2 + 3) >> 3;
|
||||
diff[2][i] = (c2 + 3) >> 3;
|
||||
diff[3][i] = (d2 + 3) >> 3;
|
||||
offset += 4;
|
||||
}
|
||||
|
||||
return diff;
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,640 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Brooss, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.webp.vp8;
|
||||
|
||||
final class LoopFilter {
|
||||
private static int abs(int v) {
|
||||
return v < 0 ? -v : v;
|
||||
}
|
||||
|
||||
private static int c(int v) {
|
||||
return v < -128 ? -128 : (v > 127 ? 127 : v);
|
||||
}
|
||||
|
||||
private static int common_adjust(boolean use_outer_taps, /* filter is 2 or 4 taps wide */ Segment seg) {
|
||||
int p1 = u2s(seg.P1); /* retrieve and convert all 4 pixels */
|
||||
int p0 = u2s(seg.P0);
|
||||
int q0 = u2s(seg.Q0);
|
||||
int q1 = u2s(seg.Q1);
|
||||
|
||||
/*
|
||||
* Disregarding clamping, when "use_outer_taps" is false, "a" is
|
||||
* 3*(q0-p0). Since we are about to divide "a" by 8, in this case we end
|
||||
* up multiplying the edge difference by 5/8. When "use_outer_taps" is
|
||||
* true (as for the simple filter), "a" is p1 - 3*p0 + 3*q0 - q1, which
|
||||
* can be thought of as a refinement of 2*(q0 - p0) and the adjustment
|
||||
* is something like (q0 - p0)/4.
|
||||
*/
|
||||
int a = c((use_outer_taps ? c(p1 - q1) : 0) + 3 * (q0 - p0));
|
||||
/*
|
||||
* b is used to balance the rounding of a/8 in the case where the
|
||||
* "fractional" part "f" of a/8 is exactly 1/2.
|
||||
*/
|
||||
int b = (c(a + 3)) >> 3;
|
||||
/*
|
||||
* Divide a by 8, rounding up when f >= 1/2. Although not strictly part
|
||||
* of the "C" language, the right-shift is assumed to propagate the sign
|
||||
* bit.
|
||||
*/
|
||||
a = c(a + 4) >> 3;
|
||||
/* Subtract "a" from q0, "bringing it closer" to p0. */
|
||||
seg.Q0 = s2u(q0 - a);
|
||||
/*
|
||||
* Add "a" (with adjustment "b") to p0, "bringing it closer" to q0. The
|
||||
* clamp of "a+b", while present in the reference decoder, is
|
||||
* superfluous; we have -16 <= a <= 15 at this point.
|
||||
*/
|
||||
seg.P0 = s2u(p0 + b);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
/*
|
||||
* All functions take (among other things) a segment (of length at most 4 +
|
||||
* 4 = 8) symmetrically straddling an edge. The pixel values (or pointers)
|
||||
* are always given in order, from the "beforemost" to the "aftermost". So,
|
||||
* for a horizontal edge (written "|"), an 8-pixel segment would be ordered
|
||||
* p3 p2 p1 p0 | q0 q1 q2 q3.
|
||||
*/
|
||||
/*
|
||||
* Filtering is disabled if the difference between any two adjacent
|
||||
* "interior" pixels in the 8-pixel segment exceeds the relevant threshold
|
||||
* (I). A more complex thresholding calculation is done for the group of
|
||||
* four pixels that straddle the edge, in line with the calculation in
|
||||
* simple_segment() above.
|
||||
*/
|
||||
private static boolean filter_yes(int I, /* limit on interior differences */
|
||||
int E, /* limit at the edge */
|
||||
int p3, int p2, int p1, int p0, /* pixels before edge */
|
||||
int q0, int q1, int q2, int q3 /* pixels after edge */
|
||||
) {
|
||||
return (abs(p0 - q0) * 2 + abs(p1 - q1) / 2) <= E && abs(p3 - p2) <= I
|
||||
&& abs(p2 - p1) <= I && abs(p1 - p0) <= I && abs(q3 - q2) <= I
|
||||
&& abs(q2 - q1) <= I && abs(q1 - q0) <= I;
|
||||
}
|
||||
|
||||
private static Segment getSegH(SubBlock rsb, SubBlock lsb, int a) {
|
||||
Segment seg = new Segment();
|
||||
int[][] rdest = rsb.getDest();
|
||||
int[][] ldest = lsb.getDest();
|
||||
seg.P0 = ldest[3][a];
|
||||
seg.P1 = ldest[2][a];
|
||||
seg.P2 = ldest[1][a];
|
||||
seg.P3 = ldest[0][a];
|
||||
seg.Q0 = rdest[0][a];
|
||||
seg.Q1 = rdest[1][a];
|
||||
seg.Q2 = rdest[2][a];
|
||||
seg.Q3 = rdest[3][a];
|
||||
return seg;
|
||||
}
|
||||
|
||||
private static Segment getSegV(SubBlock bsb, SubBlock tsb, int a) {
|
||||
Segment seg = new Segment();
|
||||
int[][] bdest = bsb.getDest();
|
||||
int[][] tdest = tsb.getDest();
|
||||
|
||||
seg.P0 = tdest[a][3];
|
||||
seg.P1 = tdest[a][2];
|
||||
seg.P2 = tdest[a][1];
|
||||
seg.P3 = tdest[a][0];
|
||||
seg.Q0 = bdest[a][0];
|
||||
seg.Q1 = bdest[a][1];
|
||||
seg.Q2 = bdest[a][2];
|
||||
seg.Q3 = bdest[a][3];
|
||||
return seg;
|
||||
}
|
||||
|
||||
/*
|
||||
* Filtering is altered if (at least) one of the differences on either side
|
||||
* of the edge exceeds a threshold (we have "high edge variance").
|
||||
*/
|
||||
private static boolean hev(int threshold, int p1, int p0, /*
|
||||
* pixels before
|
||||
* edge
|
||||
*/
|
||||
int q0, int q1 /* pixels after edge */
|
||||
) {
|
||||
return abs(p1 - p0) > threshold || abs(q1 - q0) > threshold;
|
||||
}
|
||||
|
||||
public static void loopFilter(VP8Frame frame) {
|
||||
// frame.fireLFProgressUpdate(0);
|
||||
if (frame.getFilterType() == 2) {
|
||||
loopFilterUV(frame);
|
||||
// frame.fireLFProgressUpdate(50);
|
||||
loopFilterY(frame);
|
||||
} else if (frame.getFilterType() == 1) {
|
||||
loopFilterSimple(frame);
|
||||
}
|
||||
// frame.fireLFProgressUpdate(100);
|
||||
}
|
||||
|
||||
private static void loopFilterSimple(VP8Frame frame) {
|
||||
for (int y = 0; y < frame.getMacroBlockRows(); y++) {
|
||||
// frame.fireLFProgressUpdate((100.0f * ((float) (y + 1) / (float) (frame
|
||||
// .getMacroBlockRows()))));
|
||||
for (int x = 0; x < frame.getMacroBlockCols(); x++) {
|
||||
// System.out.println("x: "+x+" y: "+y);
|
||||
MacroBlock rmb = frame.getMacroBlock(x, y);
|
||||
MacroBlock bmb = frame.getMacroBlock(x, y);
|
||||
|
||||
int loop_filter_level = rmb.getFilterLevel();
|
||||
if (loop_filter_level != 0) {
|
||||
int interior_limit = rmb.getFilterLevel();
|
||||
|
||||
int sharpnessLevel = frame.getSharpnessLevel();
|
||||
if (sharpnessLevel > 0) {
|
||||
interior_limit >>= sharpnessLevel > 4 ? 2 : 1;
|
||||
if (interior_limit > 9 - sharpnessLevel) {
|
||||
interior_limit = 9 - sharpnessLevel;
|
||||
}
|
||||
}
|
||||
if (interior_limit == 0) {
|
||||
interior_limit = 1;
|
||||
}
|
||||
|
||||
/* Luma and Chroma use the same inter-subblock edge limit */
|
||||
int sub_bedge_limit = (loop_filter_level * 2) + interior_limit;
|
||||
if (sub_bedge_limit < 1) {
|
||||
sub_bedge_limit = 1;
|
||||
}
|
||||
|
||||
/* Luma and Chroma use the same inter-macroblock edge limit */
|
||||
int mbedge_limit = sub_bedge_limit + 4;
|
||||
|
||||
// left
|
||||
if (x > 0) {
|
||||
MacroBlock lmb = frame.getMacroBlock(x - 1, y);
|
||||
for (int b = 0; b < 4; b++) {
|
||||
SubBlock rsb = rmb.getSubBlock(SubBlock.PLANE.Y1, 0, b);
|
||||
SubBlock lsb = lmb.getSubBlock(SubBlock.PLANE.Y1, 3, b);
|
||||
for (int a = 0; a < 4; a++) {
|
||||
Segment seg = getSegH(rsb, lsb, a);
|
||||
// MBfilter(hev_threshold, interior_limit,
|
||||
// mbedge_limit, seg);
|
||||
// System.out.println(mbedge_limit);
|
||||
simple_segment(mbedge_limit, seg);
|
||||
setSegH(rsb, lsb, seg, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sb left
|
||||
if (!rmb.isSkip_inner_lf()) {
|
||||
|
||||
for (int a = 1; a < 4; a++) {
|
||||
for (int b = 0; b < 4; b++) {
|
||||
SubBlock lsb = rmb.getSubBlock(SubBlock.PLANE.Y1,
|
||||
a - 1, b);
|
||||
SubBlock rsb = rmb.getSubBlock(SubBlock.PLANE.Y1,
|
||||
a, b);
|
||||
for (int c = 0; c < 4; c++) {
|
||||
// System.out.println("sbleft a:"+a+" b:"+b+" c:"+c);
|
||||
Segment seg = getSegH(rsb, lsb, c);
|
||||
simple_segment(sub_bedge_limit, seg);
|
||||
// System.out.println(sub_bedge_limit);
|
||||
// subblock_filter(hev_threshold,interior_limit,sub_bedge_limit,
|
||||
// seg);
|
||||
setSegH(rsb, lsb, seg, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// top
|
||||
if (y > 0) {
|
||||
MacroBlock tmb = frame.getMacroBlock(x, y - 1);
|
||||
for (int b = 0; b < 4; b++) {
|
||||
SubBlock tsb = tmb.getSubBlock(SubBlock.PLANE.Y1, b, 3);
|
||||
SubBlock bsb = bmb.getSubBlock(SubBlock.PLANE.Y1, b, 0);
|
||||
for (int a = 0; a < 4; a++) {
|
||||
Segment seg = getSegV(bsb, tsb, a);
|
||||
simple_segment(mbedge_limit, seg);
|
||||
// System.out.println(mbedge_limit);
|
||||
// MBfilter(hev_threshold, interior_limit,
|
||||
// mbedge_limit, seg);
|
||||
setSegV(bsb, tsb, seg, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sb top
|
||||
if (!rmb.isSkip_inner_lf()) {
|
||||
for (int a = 1; a < 4; a++) {
|
||||
for (int b = 0; b < 4; b++) {
|
||||
SubBlock tsb = bmb.getSubBlock(SubBlock.PLANE.Y1,
|
||||
b, a - 1);
|
||||
SubBlock bsb = bmb.getSubBlock(SubBlock.PLANE.Y1,
|
||||
b, a);
|
||||
for (int c = 0; c < 4; c++) {
|
||||
// System.out.println("sbtop");
|
||||
Segment seg = getSegV(bsb, tsb, c);
|
||||
simple_segment(sub_bedge_limit, seg);
|
||||
// System.out.println(sub_bedge_limit);
|
||||
// subblock_filter(hev_threshold,interior_limit,sub_bedge_limit,
|
||||
// seg);
|
||||
setSegV(bsb, tsb, seg, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void loopFilterUV(VP8Frame frame) {
|
||||
for (int y = 0; y < frame.getMacroBlockRows(); y++) {
|
||||
// frame.fireLFProgressUpdate((100.0f * ((float) (y + 1) / (float) (frame
|
||||
// .getMacroBlockRows()))) / 2);
|
||||
for (int x = 0; x < frame.getMacroBlockCols(); x++) {
|
||||
MacroBlock rmb = frame.getMacroBlock(x, y);
|
||||
MacroBlock bmb = frame.getMacroBlock(x, y);
|
||||
int sharpnessLevel = frame.getSharpnessLevel();
|
||||
int loop_filter_level = rmb.getFilterLevel();
|
||||
if (loop_filter_level != 0) {
|
||||
int interior_limit = rmb.getFilterLevel();
|
||||
if (sharpnessLevel > 0) {
|
||||
interior_limit >>= sharpnessLevel > 4 ? 2 : 1;
|
||||
if (interior_limit > 9 - sharpnessLevel) {
|
||||
interior_limit = 9 - sharpnessLevel;
|
||||
}
|
||||
}
|
||||
if (interior_limit == 0) {
|
||||
interior_limit = 1;
|
||||
}
|
||||
|
||||
int hev_threshold = 0;
|
||||
if (frame.getFrameType() == 0) /* current frame is a key frame */ {
|
||||
if (loop_filter_level >= 40) {
|
||||
hev_threshold = 2;
|
||||
} else if (loop_filter_level >= 15) {
|
||||
hev_threshold = 1;
|
||||
}
|
||||
} else /* current frame is an interframe */ {
|
||||
if (loop_filter_level >= 40) {
|
||||
hev_threshold = 3;
|
||||
} else if (loop_filter_level >= 20) {
|
||||
hev_threshold = 2;
|
||||
} else if (loop_filter_level >= 15) {
|
||||
hev_threshold = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Luma and Chroma use the same inter-macroblock edge limit */
|
||||
int mbedge_limit = ((loop_filter_level + 2) * 2)
|
||||
+ interior_limit;
|
||||
/* Luma and Chroma use the same inter-subblock edge limit */
|
||||
int sub_bedge_limit = (loop_filter_level * 2) + interior_limit;
|
||||
|
||||
if (x > 0) {
|
||||
MacroBlock lmb = frame.getMacroBlock(x - 1, y);
|
||||
for (int b = 0; b < 2; b++) {
|
||||
SubBlock rsbU = rmb.getSubBlock(SubBlock.PLANE.U, 0, b);
|
||||
SubBlock lsbU = lmb.getSubBlock(SubBlock.PLANE.U, 1, b);
|
||||
SubBlock rsbV = rmb.getSubBlock(SubBlock.PLANE.V, 0, b);
|
||||
SubBlock lsbV = lmb.getSubBlock(SubBlock.PLANE.V, 1, b);
|
||||
for (int a = 0; a < 4; a++) {
|
||||
Segment seg = getSegH(rsbU, lsbU, a);
|
||||
MBfilter(hev_threshold, interior_limit,
|
||||
mbedge_limit, seg);
|
||||
setSegH(rsbU, lsbU, seg, a);
|
||||
seg = getSegH(rsbV, lsbV, a);
|
||||
MBfilter(hev_threshold, interior_limit,
|
||||
mbedge_limit, seg);
|
||||
setSegH(rsbV, lsbV, seg, a);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
// sb left
|
||||
|
||||
if (!rmb.isSkip_inner_lf()) {
|
||||
for (int a = 1; a < 2; a++) {
|
||||
for (int b = 0; b < 2; b++) {
|
||||
SubBlock lsbU = rmb.getSubBlock(SubBlock.PLANE.U,
|
||||
a - 1, b);
|
||||
SubBlock rsbU = rmb.getSubBlock(SubBlock.PLANE.U,
|
||||
a, b);
|
||||
SubBlock lsbV = rmb.getSubBlock(SubBlock.PLANE.V,
|
||||
a - 1, b);
|
||||
SubBlock rsbV = rmb.getSubBlock(SubBlock.PLANE.V,
|
||||
a, b);
|
||||
for (int c = 0; c < 4; c++) {
|
||||
Segment seg = getSegH(rsbU, lsbU, c);
|
||||
subblock_filter(hev_threshold, interior_limit,
|
||||
sub_bedge_limit, seg);
|
||||
setSegH(rsbU, lsbU, seg, c);
|
||||
seg = getSegH(rsbV, lsbV, c);
|
||||
subblock_filter(hev_threshold, interior_limit,
|
||||
sub_bedge_limit, seg);
|
||||
setSegH(rsbV, lsbV, seg, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// top
|
||||
if (y > 0) {
|
||||
MacroBlock tmb = frame.getMacroBlock(x, y - 1);
|
||||
for (int b = 0; b < 2; b++) {
|
||||
SubBlock tsbU = tmb.getSubBlock(SubBlock.PLANE.U, b, 1);
|
||||
SubBlock bsbU = bmb.getSubBlock(SubBlock.PLANE.U, b, 0);
|
||||
SubBlock tsbV = tmb.getSubBlock(SubBlock.PLANE.V, b, 1);
|
||||
SubBlock bsbV = bmb.getSubBlock(SubBlock.PLANE.V, b, 0);
|
||||
for (int a = 0; a < 4; a++) {
|
||||
// System.out.println("l");
|
||||
Segment seg = getSegV(bsbU, tsbU, a);
|
||||
MBfilter(hev_threshold, interior_limit,
|
||||
mbedge_limit, seg);
|
||||
setSegV(bsbU, tsbU, seg, a);
|
||||
seg = getSegV(bsbV, tsbV, a);
|
||||
MBfilter(hev_threshold, interior_limit,
|
||||
mbedge_limit, seg);
|
||||
setSegV(bsbV, tsbV, seg, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
// sb top
|
||||
|
||||
if (!rmb.isSkip_inner_lf()) {
|
||||
for (int a = 1; a < 2; a++) {
|
||||
for (int b = 0; b < 2; b++) {
|
||||
SubBlock tsbU = bmb.getSubBlock(SubBlock.PLANE.U,
|
||||
b, a - 1);
|
||||
SubBlock bsbU = bmb.getSubBlock(SubBlock.PLANE.U,
|
||||
b, a);
|
||||
SubBlock tsbV = bmb.getSubBlock(SubBlock.PLANE.V,
|
||||
b, a - 1);
|
||||
SubBlock bsbV = bmb.getSubBlock(SubBlock.PLANE.V,
|
||||
b, a);
|
||||
for (int c = 0; c < 4; c++) {
|
||||
Segment seg = getSegV(bsbU, tsbU, c);
|
||||
subblock_filter(hev_threshold, interior_limit,
|
||||
sub_bedge_limit, seg);
|
||||
setSegV(bsbU, tsbU, seg, c);
|
||||
seg = getSegV(bsbV, tsbV, c);
|
||||
subblock_filter(hev_threshold, interior_limit,
|
||||
sub_bedge_limit, seg);
|
||||
setSegV(bsbV, tsbV, seg, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void loopFilterY(VP8Frame frame) {
|
||||
for (int y = 0; y < frame.getMacroBlockRows(); y++) {
|
||||
// frame.fireLFProgressUpdate(50 + (100.0f * ((float) (y + 1) / (float) (frame
|
||||
// .getMacroBlockRows()))) / 2);
|
||||
for (int x = 0; x < frame.getMacroBlockCols(); x++) {
|
||||
MacroBlock rmb = frame.getMacroBlock(x, y);
|
||||
MacroBlock bmb = frame.getMacroBlock(x, y);
|
||||
int sharpnessLevel = frame.getSharpnessLevel();
|
||||
int loop_filter_level = rmb.getFilterLevel();
|
||||
|
||||
if (loop_filter_level != 0) {
|
||||
int interior_limit = rmb.getFilterLevel();
|
||||
|
||||
if (sharpnessLevel > 0) {
|
||||
interior_limit >>= sharpnessLevel > 4 ? 2 : 1;
|
||||
if (interior_limit > 9 - sharpnessLevel) {
|
||||
interior_limit = 9 - sharpnessLevel;
|
||||
}
|
||||
}
|
||||
if (interior_limit == 0) {
|
||||
interior_limit = 1;
|
||||
}
|
||||
|
||||
int hev_threshold = 0;
|
||||
if (frame.getFrameType() == 0) /* current frame is a key frame */ {
|
||||
if (loop_filter_level >= 40) {
|
||||
hev_threshold = 2;
|
||||
} else if (loop_filter_level >= 15) {
|
||||
hev_threshold = 1;
|
||||
}
|
||||
} else /* current frame is an interframe */ {
|
||||
if (loop_filter_level >= 40) {
|
||||
hev_threshold = 3;
|
||||
} else if (loop_filter_level >= 20) {
|
||||
hev_threshold = 2;
|
||||
} else if (loop_filter_level >= 15) {
|
||||
hev_threshold = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Luma and Chroma use the same inter-macroblock edge limit */
|
||||
int mbedge_limit = ((loop_filter_level + 2) * 2)
|
||||
+ interior_limit;
|
||||
/* Luma and Chroma use the same inter-subblock edge limit */
|
||||
int sub_bedge_limit = (loop_filter_level * 2) + interior_limit;
|
||||
|
||||
// left
|
||||
if (x > 0) {
|
||||
MacroBlock lmb = frame.getMacroBlock(x - 1, y);
|
||||
for (int b = 0; b < 4; b++) {
|
||||
SubBlock rsb = rmb.getSubBlock(SubBlock.PLANE.Y1, 0, b);
|
||||
SubBlock lsb = lmb.getSubBlock(SubBlock.PLANE.Y1, 3, b);
|
||||
for (int a = 0; a < 4; a++) {
|
||||
Segment seg = getSegH(rsb, lsb, a);
|
||||
MBfilter(hev_threshold, interior_limit,
|
||||
mbedge_limit, seg);
|
||||
setSegH(rsb, lsb, seg, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
// sb left
|
||||
if (!rmb.isSkip_inner_lf()) {
|
||||
for (int a = 1; a < 4; a++) {
|
||||
for (int b = 0; b < 4; b++) {
|
||||
SubBlock lsb = rmb.getSubBlock(SubBlock.PLANE.Y1,
|
||||
a - 1, b);
|
||||
SubBlock rsb = rmb.getSubBlock(SubBlock.PLANE.Y1,
|
||||
a, b);
|
||||
for (int c = 0; c < 4; c++) {
|
||||
// System.out.println("sbleft a:"+a+" b:"+b+" c:"+c);
|
||||
Segment seg = getSegH(rsb, lsb, c);
|
||||
subblock_filter(hev_threshold, interior_limit,
|
||||
sub_bedge_limit, seg);
|
||||
setSegH(rsb, lsb, seg, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// top
|
||||
if (y > 0) {
|
||||
MacroBlock tmb = frame.getMacroBlock(x, y - 1);
|
||||
for (int b = 0; b < 4; b++) {
|
||||
SubBlock tsb = tmb.getSubBlock(SubBlock.PLANE.Y1, b, 3);
|
||||
SubBlock bsb = bmb.getSubBlock(SubBlock.PLANE.Y1, b, 0);
|
||||
for (int a = 0; a < 4; a++) {
|
||||
Segment seg = getSegV(bsb, tsb, a);
|
||||
MBfilter(hev_threshold, interior_limit,
|
||||
mbedge_limit, seg);
|
||||
setSegV(bsb, tsb, seg, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
// sb top
|
||||
if (!rmb.isSkip_inner_lf()) {
|
||||
for (int a = 1; a < 4; a++) {
|
||||
for (int b = 0; b < 4; b++) {
|
||||
SubBlock tsb = bmb.getSubBlock(SubBlock.PLANE.Y1,
|
||||
b, a - 1);
|
||||
SubBlock bsb = bmb.getSubBlock(SubBlock.PLANE.Y1,
|
||||
b, a);
|
||||
for (int c = 0; c < 4; c++) {
|
||||
Segment seg = getSegV(bsb, tsb, c);
|
||||
subblock_filter(hev_threshold, interior_limit,
|
||||
sub_bedge_limit, seg);
|
||||
setSegV(bsb, tsb, seg, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void MBfilter(int hev_threshold, /* detect high edge variance */
|
||||
int interior_limit, /* possibly disable filter */
|
||||
int edge_limit, Segment seg) {
|
||||
int p3 = u2s(seg.P3), p2 = u2s(seg.P2), p1 = u2s(seg.P1), p0 = u2s(seg.P0);
|
||||
int q0 = u2s(seg.Q0), q1 = u2s(seg.Q1), q2 = u2s(seg.Q2), q3 = u2s(seg.Q3);
|
||||
if (filter_yes(interior_limit, edge_limit, q3, q2, q1, q0, p0, p1, p2,
|
||||
p3)) {
|
||||
if (!hev(hev_threshold, p1, p0, q0, q1)) {
|
||||
// Same as the initial calculation in "common_adjust",
|
||||
// w is something like twice the edge difference
|
||||
int w = c(c(p1 - q1) + 3 * (q0 - p0));
|
||||
|
||||
// 9/64 is approximately 9/63 = 1/7 and 1<<7 = 128 = 2*64.
|
||||
// So this a, used to adjust the pixels adjacent to the edge,
|
||||
// is something like 3/7 the edge difference.
|
||||
int a = (27 * w + 63) >> 7;
|
||||
|
||||
seg.Q0 = s2u(q0 - a);
|
||||
seg.P0 = s2u(p0 + a);
|
||||
// Next two are adjusted by 2/7 the edge difference
|
||||
a = (18 * w + 63) >> 7;
|
||||
// System.out.println("a: "+a);
|
||||
seg.Q1 = s2u(q1 - a);
|
||||
seg.P1 = s2u(p1 + a);
|
||||
// Last two are adjusted by 1/7 the edge difference
|
||||
a = (9 * w + 63) >> 7;
|
||||
seg.Q2 = s2u(q2 - a);
|
||||
seg.P2 = s2u(p2 + a);
|
||||
} else
|
||||
// if hev, do simple filter
|
||||
{
|
||||
common_adjust(true, seg); // using outer taps
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Clamp, then convert signed number back to pixel value. */
|
||||
private static int s2u(int v) {
|
||||
return c(v) + 128;
|
||||
}
|
||||
|
||||
private static void setSegH(SubBlock rsb, SubBlock lsb, Segment seg, int a) {
|
||||
int[][] rdest = rsb.getDest();
|
||||
int[][] ldest = lsb.getDest();
|
||||
ldest[3][a] = seg.P0;
|
||||
ldest[2][a] = seg.P1;
|
||||
ldest[1][a] = seg.P2;
|
||||
ldest[0][a] = seg.P3;
|
||||
rdest[0][a] = seg.Q0;
|
||||
rdest[1][a] = seg.Q1;
|
||||
rdest[2][a] = seg.Q2;
|
||||
rdest[3][a] = seg.Q3;
|
||||
|
||||
}
|
||||
|
||||
private static void setSegV(SubBlock bsb, SubBlock tsb, Segment seg, int a) {
|
||||
int[][] bdest = bsb.getDest();
|
||||
int[][] tdest = tsb.getDest();
|
||||
tdest[a][3] = seg.P0;
|
||||
tdest[a][2] = seg.P1;
|
||||
tdest[a][1] = seg.P2;
|
||||
tdest[a][0] = seg.P3;
|
||||
bdest[a][0] = seg.Q0;
|
||||
bdest[a][1] = seg.Q1;
|
||||
bdest[a][2] = seg.Q2;
|
||||
bdest[a][3] = seg.Q3;
|
||||
|
||||
}
|
||||
|
||||
private static void simple_segment(int edge_limit, /*
|
||||
* do nothing if edge
|
||||
* difference exceeds
|
||||
* limit
|
||||
*/
|
||||
Segment seg) {
|
||||
if ((abs(seg.P0 - seg.Q0) * 2 + abs(seg.P1 - seg.Q1) / 2) <= edge_limit) {
|
||||
common_adjust(true, seg); /* use outer taps */
|
||||
}
|
||||
else {
|
||||
// TODO?
|
||||
}
|
||||
}
|
||||
|
||||
private static void subblock_filter(int hev_threshold, /*
|
||||
* detect high edge
|
||||
* variance
|
||||
*/
|
||||
int interior_limit, /* possibly disable filter */
|
||||
int edge_limit, Segment seg) {
|
||||
int p3 = u2s(seg.P3), p2 = u2s(seg.P2), p1 = u2s(seg.P1), p0 = u2s(seg.P0);
|
||||
int q0 = u2s(seg.Q0), q1 = u2s(seg.Q1), q2 = u2s(seg.Q2), q3 = u2s(seg.Q3);
|
||||
if (filter_yes(interior_limit, edge_limit, q3, q2, q1, q0, p0, p1, p2, p3)) {
|
||||
boolean hv = hev(hev_threshold, p1, p0, q0, q1);
|
||||
int a = (common_adjust(hv, seg) + 1) >> 1;
|
||||
if (!hv) {
|
||||
seg.Q1 = s2u(q1 - a);
|
||||
seg.P1 = s2u(p1 + a);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// TODO?
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Convert pixel value (0 <= v <= 255) to an 8-bit signed number. */
|
||||
private static int u2s(int v) {
|
||||
return v - 128;
|
||||
}
|
||||
}
|
@ -0,0 +1,840 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Brooss, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.webp.vp8;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
final class MacroBlock {
|
||||
|
||||
private int filterLevel;
|
||||
private boolean keepDebugInfo = false;
|
||||
private int segmentId;
|
||||
private int skipCoeff;
|
||||
private boolean skipInnerLoopFilter;
|
||||
SubBlock[][] uSubBlocks;
|
||||
private int uVFilterLevel;
|
||||
|
||||
private int uvMode;
|
||||
SubBlock[][] vSubBlocks;
|
||||
private int x, y;
|
||||
SubBlock y2SubBlock;
|
||||
private int yMode;
|
||||
SubBlock[][] ySubBlocks;
|
||||
|
||||
MacroBlock(int x, int y, boolean keepDebugInfo) {
|
||||
this.x = x - 1;
|
||||
this.y = y - 1;
|
||||
this.keepDebugInfo = keepDebugInfo;
|
||||
|
||||
ySubBlocks = new SubBlock[4][4];
|
||||
uSubBlocks = new SubBlock[2][2];
|
||||
vSubBlocks = new SubBlock[2][2];
|
||||
SubBlock above = null;
|
||||
SubBlock left = null;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
left = null;
|
||||
above = null;
|
||||
if (j > 0) {
|
||||
left = ySubBlocks[j - 1][i];
|
||||
}
|
||||
if (i > 0) {
|
||||
above = ySubBlocks[j][i - 1];
|
||||
}
|
||||
ySubBlocks[j][i] = new SubBlock(this, above, left,
|
||||
SubBlock.PLANE.Y1);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
left = null;
|
||||
above = null;
|
||||
if (j > 0) {
|
||||
left = uSubBlocks[j - 1][i];
|
||||
}
|
||||
if (i > 0) {
|
||||
above = uSubBlocks[j][i - 1];
|
||||
}
|
||||
uSubBlocks[j][i] = new SubBlock(this, above, left,
|
||||
SubBlock.PLANE.U);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
left = null;
|
||||
above = null;
|
||||
if (j > 0) {
|
||||
left = vSubBlocks[j - 1][i];
|
||||
}
|
||||
if (i > 0) {
|
||||
above = vSubBlocks[j][i - 1];
|
||||
}
|
||||
vSubBlocks[j][i] = new SubBlock(this, above, left,
|
||||
SubBlock.PLANE.V);
|
||||
}
|
||||
}
|
||||
y2SubBlock = new SubBlock(this, null, null, SubBlock.PLANE.Y2);
|
||||
|
||||
}
|
||||
|
||||
public void decodeMacroBlock(VP8Frame frame) throws IOException {
|
||||
MacroBlock mb = this;
|
||||
if (mb.getSkipCoeff() > 0) {
|
||||
if (mb.getYMode() != Globals.B_PRED) {
|
||||
mb.skipInnerLoopFilter = true;
|
||||
}
|
||||
} else if (mb.getYMode() != Globals.B_PRED) {
|
||||
decodeMacroBlockTokens(frame, true);
|
||||
} else {
|
||||
decodeMacroBlockTokens(frame, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void decodeMacroBlockTokens(VP8Frame frame, boolean withY2)
|
||||
throws IOException {
|
||||
skipInnerLoopFilter = false;
|
||||
if (withY2) {
|
||||
skipInnerLoopFilter = skipInnerLoopFilter
|
||||
| decodePlaneTokens(frame, 1, SubBlock.PLANE.Y2, false);
|
||||
}
|
||||
skipInnerLoopFilter = skipInnerLoopFilter
|
||||
| decodePlaneTokens(frame, 4, SubBlock.PLANE.Y1, withY2);
|
||||
skipInnerLoopFilter = skipInnerLoopFilter
|
||||
| decodePlaneTokens(frame, 2, SubBlock.PLANE.U, false);
|
||||
skipInnerLoopFilter = skipInnerLoopFilter
|
||||
| decodePlaneTokens(frame, 2, SubBlock.PLANE.V, false);
|
||||
skipInnerLoopFilter = !skipInnerLoopFilter;
|
||||
}
|
||||
|
||||
private boolean decodePlaneTokens(VP8Frame frame, int dimentions,
|
||||
SubBlock.PLANE plane, boolean withY2) throws IOException {
|
||||
MacroBlock mb = this;
|
||||
boolean r = false;
|
||||
for (int y = 0; y < dimentions; y++) {
|
||||
for (int x = 0; x < dimentions; x++) {
|
||||
int L = 0;
|
||||
int A = 0;
|
||||
int lc = 0;
|
||||
SubBlock sb = mb.getSubBlock(plane, x, y);
|
||||
SubBlock left = frame.getLeftSubBlock(sb, plane);
|
||||
SubBlock above = frame.getAboveSubBlock(sb, plane);
|
||||
if (left.hasNoZeroToken()) {
|
||||
|
||||
L = 1;
|
||||
}
|
||||
|
||||
lc += L;
|
||||
|
||||
if (above.hasNoZeroToken()) {
|
||||
|
||||
A = 1;
|
||||
}
|
||||
|
||||
lc += A;
|
||||
sb.decodeSubBlock(frame.getTokenBoolDecoder(),
|
||||
frame.getCoefProbs(), lc,
|
||||
SubBlock.planeToType(plane, withY2), withY2);
|
||||
r = r | sb.hasNoZeroToken();
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
public void dequantMacroBlock(VP8Frame frame) {
|
||||
MacroBlock mb = this;
|
||||
if (mb.getYMode() != Globals.B_PRED) {
|
||||
SubBlock sb = mb.getY2SubBlock();
|
||||
int acQValue = frame.getSegmentQuants().getSegQuants()[this.getSegmentId()]
|
||||
.getY2ac_delta_q();
|
||||
int dcQValue = frame.getSegmentQuants().getSegQuants()[this.getSegmentId()].getY2dc();
|
||||
|
||||
int input[] = new int[16];
|
||||
input[0] = sb.getTokens()[0] * dcQValue;
|
||||
|
||||
for (int x = 1; x < 16; x++) {
|
||||
input[x] = sb.getTokens()[x] * acQValue;
|
||||
}
|
||||
|
||||
sb.setDiff(IDCT.iwalsh4x4(input));
|
||||
for (int j = 0; j < 4; j++) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
SubBlock ysb = mb.getYSubBlock(i, j);
|
||||
ysb.dequantSubBlock(frame, sb.getDiff()[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
mb.predictY(frame);
|
||||
mb.predictUV(frame);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
SubBlock uvsb = mb.getUSubBlock(j, i);
|
||||
uvsb.dequantSubBlock(frame, null);
|
||||
uvsb = mb.getVSubBlock(i, j);
|
||||
uvsb.dequantSubBlock(frame, null);
|
||||
}
|
||||
}
|
||||
mb.recon_mb();
|
||||
|
||||
} else {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
SubBlock sb = mb.getYSubBlock(i, j);
|
||||
sb.dequantSubBlock(frame, null);
|
||||
sb.predict(frame);
|
||||
sb.reconstruct();
|
||||
}
|
||||
}
|
||||
mb.predictUV(frame);
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
SubBlock sb = mb.getUSubBlock(j, i);
|
||||
sb.dequantSubBlock(frame, null);
|
||||
sb.reconstruct();
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
SubBlock sb = mb.getVSubBlock(j, i);
|
||||
sb.dequantSubBlock(frame, null);
|
||||
sb.reconstruct();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void drawDebug() {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
SubBlock sb = ySubBlocks[i][0];
|
||||
sb.drawDebugH();
|
||||
sb = ySubBlocks[0][j];
|
||||
sb.drawDebugV();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getDebugString() {
|
||||
String r = " YMode: " + Globals.getModeAsString(yMode);
|
||||
r = r + "\n UVMode: " + Globals.getModeAsString(uvMode);
|
||||
r = r + "\n SegmentID: " + segmentId;
|
||||
r = r + "\n Filter Level: " + filterLevel;
|
||||
r = r + "\n UV Filter Level: " + uVFilterLevel;
|
||||
r = r + "\n Skip Coeff: " + skipCoeff;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
public int getFilterLevel() {
|
||||
return this.filterLevel;
|
||||
}
|
||||
|
||||
public SubBlock getBottomSubBlock(int x, SubBlock.PLANE plane) {
|
||||
switch (plane) {
|
||||
case Y1:
|
||||
return ySubBlocks[x][3];
|
||||
case U:
|
||||
return uSubBlocks[x][1];
|
||||
case V:
|
||||
return vSubBlocks[x][1];
|
||||
case Y2:
|
||||
return y2SubBlock;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Bad plane: " + plane);
|
||||
}
|
||||
|
||||
public SubBlock getLeftSubBlock(int y, SubBlock.PLANE plane) {
|
||||
switch (plane) {
|
||||
case Y1:
|
||||
return ySubBlocks[0][y];
|
||||
case U:
|
||||
return uSubBlocks[0][y];
|
||||
case V:
|
||||
return vSubBlocks[0][y];
|
||||
case Y2:
|
||||
return y2SubBlock;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Bad plane: " + plane);
|
||||
}
|
||||
|
||||
public SubBlock getRightSubBlock(int y, SubBlock.PLANE plane) {
|
||||
switch (plane) {
|
||||
case Y1:
|
||||
return ySubBlocks[3][y];
|
||||
case U:
|
||||
return uSubBlocks[1][y];
|
||||
case V:
|
||||
return vSubBlocks[1][y];
|
||||
case Y2:
|
||||
return y2SubBlock;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Bad plane: " + plane);
|
||||
}
|
||||
|
||||
public int getSkipCoeff() {
|
||||
return skipCoeff;
|
||||
}
|
||||
|
||||
public SubBlock getSubBlock(SubBlock.PLANE plane, int i, int j) {
|
||||
switch (plane) {
|
||||
case Y1:
|
||||
return getYSubBlock(i, j);
|
||||
case U:
|
||||
return getUSubBlock(i, j);
|
||||
case V:
|
||||
return getVSubBlock(i, j);
|
||||
case Y2:
|
||||
return getY2SubBlock();
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Bad plane: " + plane);
|
||||
}
|
||||
|
||||
public int getSubblockX(SubBlock sb) {
|
||||
if (sb.getPlane() == SubBlock.PLANE.Y1) {
|
||||
for (int y = 0; y < 4; y++) {
|
||||
for (int x = 0; x < 4; x++) {
|
||||
if (ySubBlocks[x][y] == sb) {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (sb.getPlane() == SubBlock.PLANE.U) {
|
||||
for (int y = 0; y < 2; y++) {
|
||||
for (int x = 0; x < 2; x++) {
|
||||
if (uSubBlocks[x][y] == sb) {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (sb.getPlane() == SubBlock.PLANE.V) {
|
||||
for (int y = 0; y < 2; y++) {
|
||||
for (int x = 0; x < 2; x++) {
|
||||
if (vSubBlocks[x][y] == sb) {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (sb.getPlane() == SubBlock.PLANE.Y2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -100;
|
||||
|
||||
}
|
||||
|
||||
public int getSubblockY(SubBlock sb) {
|
||||
if (sb.getPlane() == SubBlock.PLANE.Y1) {
|
||||
for (int y = 0; y < 4; y++) {
|
||||
for (int x = 0; x < 4; x++) {
|
||||
if (ySubBlocks[x][y] == sb) {
|
||||
return y;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (sb.getPlane() == SubBlock.PLANE.U) {
|
||||
for (int y = 0; y < 2; y++) {
|
||||
for (int x = 0; x < 2; x++) {
|
||||
if (uSubBlocks[x][y] == sb) {
|
||||
return y;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (sb.getPlane() == SubBlock.PLANE.V) {
|
||||
for (int y = 0; y < 2; y++) {
|
||||
for (int x = 0; x < 2; x++) {
|
||||
if (vSubBlocks[x][y] == sb) {
|
||||
return y;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (sb.getPlane() == SubBlock.PLANE.Y2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -100;
|
||||
}
|
||||
|
||||
public SubBlock getUSubBlock(int i, int j) {
|
||||
return uSubBlocks[i][j];
|
||||
}
|
||||
|
||||
public int getUVFilterLevel() {
|
||||
return this.uVFilterLevel;
|
||||
}
|
||||
|
||||
public int getUvMode() {
|
||||
return uvMode;
|
||||
}
|
||||
|
||||
public SubBlock getVSubBlock(int i, int j) {
|
||||
return vSubBlocks[i][j];
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public SubBlock getY2SubBlock() {
|
||||
return y2SubBlock;
|
||||
}
|
||||
|
||||
public int getYMode() {
|
||||
return yMode;
|
||||
}
|
||||
|
||||
public SubBlock getYSubBlock(int i, int j) {
|
||||
return ySubBlocks[i][j];
|
||||
}
|
||||
|
||||
public boolean isKeepDebugInfo() {
|
||||
return keepDebugInfo;
|
||||
}
|
||||
|
||||
public boolean isSkip_inner_lf() {
|
||||
return skipInnerLoopFilter;
|
||||
}
|
||||
|
||||
public void predictUV(VP8Frame frame) {
|
||||
MacroBlock aboveMb = frame.getMacroBlock(x, y - 1);
|
||||
MacroBlock leftMb = frame.getMacroBlock(x - 1, y);
|
||||
|
||||
switch (this.uvMode) {
|
||||
case Globals.DC_PRED:
|
||||
// System.out.println("UV DC_PRED");
|
||||
|
||||
boolean up_available = false;
|
||||
boolean left_available = false;
|
||||
int Uaverage = 0;
|
||||
int Vaverage = 0;
|
||||
int expected_udc = 0;
|
||||
int expected_vdc = 0;
|
||||
if (x > 0) {
|
||||
left_available = true;
|
||||
}
|
||||
if (y > 0) {
|
||||
up_available = true;
|
||||
}
|
||||
if (up_available || left_available) {
|
||||
if (up_available) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
SubBlock usb = aboveMb.getUSubBlock(j, 1);
|
||||
SubBlock vsb = aboveMb.getVSubBlock(j, 1);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Uaverage += usb.getDest()[i][3];
|
||||
Vaverage += vsb.getDest()[i][3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (left_available) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
SubBlock usb = leftMb.getUSubBlock(1, j);
|
||||
SubBlock vsb = leftMb.getVSubBlock(1, j);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
Uaverage += usb.getDest()[3][i];
|
||||
Vaverage += vsb.getDest()[3][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int shift = 2;
|
||||
if (up_available) {
|
||||
shift++;
|
||||
}
|
||||
if (left_available) {
|
||||
shift++;
|
||||
}
|
||||
|
||||
expected_udc = (Uaverage + (1 << (shift - 1))) >> shift;
|
||||
expected_vdc = (Vaverage + (1 << (shift - 1))) >> shift;
|
||||
} else {
|
||||
expected_udc = 128;
|
||||
expected_vdc = 128;
|
||||
}
|
||||
|
||||
int ufill[][] = new int[4][4];
|
||||
for (int y = 0; y < 4; y++) {
|
||||
for (int x = 0; x < 4; x++) {
|
||||
ufill[x][y] = expected_udc;
|
||||
}
|
||||
}
|
||||
|
||||
int vfill[][] = new int[4][4];
|
||||
for (int y = 0; y < 4; y++) {
|
||||
for (int x = 0; x < 4; x++) {
|
||||
vfill[x][y] = expected_vdc;
|
||||
}
|
||||
}
|
||||
|
||||
for (int y = 0; y < 2; y++) {
|
||||
for (int x = 0; x < 2; x++) {
|
||||
SubBlock usb = uSubBlocks[x][y];
|
||||
SubBlock vsb = vSubBlocks[x][y];
|
||||
usb.setPredict(ufill);
|
||||
vsb.setPredict(vfill);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case Globals.V_PRED:
|
||||
// System.out.println("UV V_PRED");
|
||||
|
||||
SubBlock[] aboveUSb = new SubBlock[2];
|
||||
SubBlock[] aboveVSb = new SubBlock[2];
|
||||
for (int x = 0; x < 2; x++) {
|
||||
aboveUSb[x] = aboveMb.getUSubBlock(x, 1);
|
||||
aboveVSb[x] = aboveMb.getVSubBlock(x, 1);
|
||||
}
|
||||
|
||||
for (int y = 0; y < 2; y++) {
|
||||
for (int x = 0; x < 2; x++) {
|
||||
SubBlock usb = uSubBlocks[y][x];
|
||||
SubBlock vsb = vSubBlocks[y][x];
|
||||
int ublock[][] = new int[4][4];
|
||||
int vblock[][] = new int[4][4];
|
||||
for (int j = 0; j < 4; j++) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ublock[j][i] = aboveUSb[y]
|
||||
.getMacroBlockPredict(Globals.V_PRED)[j][3];
|
||||
vblock[j][i] = aboveVSb[y]
|
||||
.getMacroBlockPredict(Globals.V_PRED)[j][3];
|
||||
}
|
||||
}
|
||||
usb.setPredict(ublock);
|
||||
vsb.setPredict(vblock);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Globals.H_PRED:
|
||||
// System.out.println("UV H_PRED");
|
||||
|
||||
SubBlock[] leftUSb = new SubBlock[2];
|
||||
SubBlock[] leftVSb = new SubBlock[2];
|
||||
for (int x = 0; x < 2; x++) {
|
||||
leftUSb[x] = leftMb.getUSubBlock(1, x);
|
||||
leftVSb[x] = leftMb.getVSubBlock(1, x);
|
||||
}
|
||||
|
||||
for (int y = 0; y < 2; y++) {
|
||||
for (int x = 0; x < 2; x++) {
|
||||
SubBlock usb = uSubBlocks[x][y];
|
||||
SubBlock vsb = vSubBlocks[x][y];
|
||||
int ublock[][] = new int[4][4];
|
||||
int vblock[][] = new int[4][4];
|
||||
for (int j = 0; j < 4; j++) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
ublock[i][j] = leftUSb[y]
|
||||
.getMacroBlockPredict(Globals.H_PRED)[3][j];
|
||||
vblock[i][j] = leftVSb[y]
|
||||
.getMacroBlockPredict(Globals.H_PRED)[3][j];
|
||||
}
|
||||
}
|
||||
usb.setPredict(ublock);
|
||||
vsb.setPredict(vblock);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case Globals.TM_PRED:
|
||||
// TODO:
|
||||
// System.out.println("UV TM_PRED MB");
|
||||
MacroBlock ALMb = frame.getMacroBlock(x - 1, y - 1);
|
||||
SubBlock ALUSb = ALMb.getUSubBlock(1, 1);
|
||||
int alu = ALUSb.getDest()[3][3];
|
||||
SubBlock ALVSb = ALMb.getVSubBlock(1, 1);
|
||||
int alv = ALVSb.getDest()[3][3];
|
||||
|
||||
aboveUSb = new SubBlock[2];
|
||||
leftUSb = new SubBlock[2];
|
||||
aboveVSb = new SubBlock[2];
|
||||
leftVSb = new SubBlock[2];
|
||||
for (int x = 0; x < 2; x++) {
|
||||
aboveUSb[x] = aboveMb.getUSubBlock(x, 1);
|
||||
leftUSb[x] = leftMb.getUSubBlock(1, x);
|
||||
aboveVSb[x] = aboveMb.getVSubBlock(x, 1);
|
||||
leftVSb[x] = leftMb.getVSubBlock(1, x);
|
||||
}
|
||||
|
||||
for (int b = 0; b < 2; b++) {
|
||||
for (int a = 0; a < 4; a++) {
|
||||
for (int d = 0; d < 2; d++) {
|
||||
for (int c = 0; c < 4; c++) {
|
||||
|
||||
int upred = leftUSb[b].getDest()[3][a]
|
||||
+ aboveUSb[d].getDest()[c][3] - alu;
|
||||
upred = Globals.clamp(upred, 255);
|
||||
uSubBlocks[d][b].setPixel(c, a, upred);
|
||||
|
||||
int vpred = leftVSb[b].getDest()[3][a]
|
||||
+ aboveVSb[d].getDest()[c][3] - alv;
|
||||
vpred = Globals.clamp(vpred, 255);
|
||||
vSubBlocks[d][b].setPixel(c, a, vpred);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
// TODO: FixME!
|
||||
throw new AssertionError("TODO predict_mb_uv: " + this.yMode);
|
||||
}
|
||||
}
|
||||
|
||||
public void predictY(VP8Frame frame) {
|
||||
MacroBlock aboveMb = frame.getMacroBlock(x, y - 1);
|
||||
MacroBlock leftMb = frame.getMacroBlock(x - 1, y);
|
||||
|
||||
switch (this.yMode) {
|
||||
case Globals.DC_PRED:
|
||||
// System.out.println("DC_PRED");
|
||||
boolean up_available = false;
|
||||
boolean left_available = false;
|
||||
|
||||
int average = 0;
|
||||
int expected_dc = 0;
|
||||
if (x > 0) {
|
||||
left_available = true;
|
||||
}
|
||||
if (y > 0) {
|
||||
up_available = true;
|
||||
}
|
||||
|
||||
if (up_available || left_available) {
|
||||
if (up_available) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
SubBlock sb = aboveMb.getYSubBlock(j, 3);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
average += sb.getDest()[i][3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (left_available) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
SubBlock sb = leftMb.getYSubBlock(3, j);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
average += sb.getDest()[3][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int shift = 3;
|
||||
if (up_available) {
|
||||
shift++;
|
||||
}
|
||||
if (left_available) {
|
||||
shift++;
|
||||
}
|
||||
|
||||
expected_dc = (average + (1 << (shift - 1))) >> shift;
|
||||
} else {
|
||||
expected_dc = 128;
|
||||
}
|
||||
|
||||
int fill[][] = new int[4][4];
|
||||
for (int y = 0; y < 4; y++) {
|
||||
for (int x = 0; x < 4; x++) {
|
||||
fill[x][y] = expected_dc;
|
||||
}
|
||||
}
|
||||
for (int y = 0; y < 4; y++) {
|
||||
for (int x = 0; x < 4; x++) {
|
||||
SubBlock sb = ySubBlocks[x][y];
|
||||
sb.setPredict(fill);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case Globals.V_PRED:
|
||||
// System.out.println("V_PRED");
|
||||
|
||||
SubBlock[] aboveYSb = new SubBlock[4];
|
||||
for (int x = 0; x < 4; x++) {
|
||||
aboveYSb[x] = aboveMb.getYSubBlock(x, 3);
|
||||
}
|
||||
|
||||
for (int y = 0; y < 4; y++) {
|
||||
for (int x = 0; x < 4; x++) {
|
||||
SubBlock sb = ySubBlocks[x][y];
|
||||
int block[][] = new int[4][4];
|
||||
for (int j = 0; j < 4; j++) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
block[i][j] = aboveYSb[x].getPredict(
|
||||
Globals.B_VE_PRED, false)[i][3];
|
||||
}
|
||||
}
|
||||
sb.setPredict(block);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case Globals.H_PRED:
|
||||
// System.out.println("H_PRED");
|
||||
|
||||
SubBlock[] leftYSb = new SubBlock[4];
|
||||
for (int x = 0; x < 4; x++) {
|
||||
leftYSb[x] = leftMb.getYSubBlock(3, x);
|
||||
}
|
||||
|
||||
for (int y = 0; y < 4; y++) {
|
||||
for (int x = 0; x < 4; x++) {
|
||||
SubBlock sb = ySubBlocks[x][y];
|
||||
int block[][] = new int[4][4];
|
||||
for (int j = 0; j < 4; j++) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
block[i][j] = leftYSb[y].getPredict(
|
||||
Globals.B_DC_PRED, true)[3][j];
|
||||
}
|
||||
}
|
||||
sb.setPredict(block);
|
||||
}
|
||||
}
|
||||
|
||||
SubBlock[] leftUSb = new SubBlock[2];
|
||||
for (int x = 0; x < 2; x++) {
|
||||
leftUSb[x] = leftMb.getYSubBlock(1, x);
|
||||
}
|
||||
|
||||
break;
|
||||
case Globals.TM_PRED:
|
||||
// System.out.println("TM_PRED MB");
|
||||
MacroBlock ALMb = frame.getMacroBlock(x - 1, y - 1);
|
||||
SubBlock ALSb = ALMb.getYSubBlock(3, 3);
|
||||
int al = ALSb.getDest()[3][3];
|
||||
|
||||
aboveYSb = new SubBlock[4];
|
||||
leftYSb = new SubBlock[4];
|
||||
for (int x = 0; x < 4; x++) {
|
||||
aboveYSb[x] = aboveMb.getYSubBlock(x, 3);
|
||||
}
|
||||
for (int x = 0; x < 4; x++) {
|
||||
leftYSb[x] = leftMb.getYSubBlock(3, x);
|
||||
}
|
||||
fill = new int[4][4];
|
||||
|
||||
for (int b = 0; b < 4; b++) {
|
||||
for (int a = 0; a < 4; a++) {
|
||||
|
||||
for (int d = 0; d < 4; d++) {
|
||||
for (int c = 0; c < 4; c++) {
|
||||
|
||||
int pred = leftYSb[b].getDest()[3][a]
|
||||
+ aboveYSb[d].getDest()[c][3] - al;
|
||||
|
||||
ySubBlocks[d][b].setPixel(c, a,
|
||||
Globals.clamp(pred, 255));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
System.out.println("TODO predict_mb_y: " + this.yMode);
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
public void recon_mb() {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
SubBlock sb = ySubBlocks[i][j];
|
||||
sb.reconstruct();
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < 2; j++) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
SubBlock sb = uSubBlocks[i][j];
|
||||
sb.reconstruct();
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < 2; j++) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
SubBlock sb = vSubBlocks[i][j];
|
||||
sb.reconstruct();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void setFilterLevel(int value) {
|
||||
this.filterLevel = value;
|
||||
}
|
||||
|
||||
public void setSegmentId(int value) {
|
||||
this.segmentId = value;
|
||||
}
|
||||
|
||||
public void setSkipCoeff(int mbSkipCoeff) {
|
||||
skipCoeff = mbSkipCoeff;
|
||||
}
|
||||
|
||||
public void setUVFilterLevel(int value) {
|
||||
this.uVFilterLevel = value;
|
||||
}
|
||||
|
||||
public void setUvMode(int mode) {
|
||||
this.uvMode = mode;
|
||||
}
|
||||
|
||||
public void setYMode(int yMode) {
|
||||
this.yMode = yMode;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "x: " + x + "y: " + y;
|
||||
}
|
||||
|
||||
public int getSegmentId() {
|
||||
return segmentId;
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Brooss, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.webp.vp8;
|
||||
|
||||
final class Segment {
|
||||
int P0, P1, P2, P3;
|
||||
int Q0, Q1, Q2, Q3;
|
||||
|
||||
public String toString() {
|
||||
return Globals.toHex(P3) + " " + Globals.toHex(P2) + " "
|
||||
+ Globals.toHex(P1) + " " + Globals.toHex(P0) + " "
|
||||
+ Globals.toHex(Q0) + " " + Globals.toHex(Q1) + " "
|
||||
+ Globals.toHex(Q2) + " " + Globals.toHex(Q3);
|
||||
}
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Brooss, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.webp.vp8;
|
||||
|
||||
final class SegmentQuant {
|
||||
private int filterStrength;
|
||||
private int Qindex;
|
||||
private int uvac;
|
||||
private int uvdc;
|
||||
private int y1ac;
|
||||
private int y1dc;
|
||||
private int y2ac;
|
||||
private int y2dc;
|
||||
|
||||
private int clip(int val, int max) {
|
||||
int r = val;
|
||||
if (val > max) {
|
||||
r = max;
|
||||
}
|
||||
if (r < 0) {
|
||||
r = 0;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
public int getQindex() {
|
||||
return Qindex;
|
||||
}
|
||||
|
||||
public int getUvac_delta_q() {
|
||||
return uvac;
|
||||
}
|
||||
|
||||
public int getUvdc_delta_q() {
|
||||
return uvdc;
|
||||
}
|
||||
|
||||
public int getY1ac() {
|
||||
return y1ac;
|
||||
}
|
||||
|
||||
public int getY1dc() {
|
||||
return y1dc;
|
||||
}
|
||||
|
||||
public int getY2ac_delta_q() {
|
||||
return y2ac;
|
||||
}
|
||||
|
||||
public int getY2dc() {
|
||||
return y2dc;
|
||||
}
|
||||
|
||||
public void setFilterStrength(int value) {
|
||||
this.filterStrength = value;
|
||||
}
|
||||
|
||||
public void setQindex(int qindex) {
|
||||
Qindex = qindex;
|
||||
}
|
||||
|
||||
public void setUvac_delta_q(int uvac_delta_q) {
|
||||
this.uvac = Globals.vp8AcQLookup[clip(Qindex + uvac_delta_q, 127)];
|
||||
}
|
||||
|
||||
public void setUvdc_delta_q(int uvdc_delta_q) {
|
||||
this.uvdc = Globals.vp8DcQLookup[clip(Qindex + uvdc_delta_q, 127)];
|
||||
}
|
||||
|
||||
public void setY1ac() {
|
||||
this.y1ac = Globals.vp8AcQLookup[clip(Qindex, 127)];
|
||||
}
|
||||
|
||||
public void setY1dc(int y1dc) {
|
||||
this.y1dc = Globals.vp8DcQLookup[clip(Qindex + y1dc, 127)];
|
||||
this.setY1ac();
|
||||
}
|
||||
|
||||
public void setY2ac_delta_q(int y2ac_delta_q) {
|
||||
this.y2ac = Globals.vp8AcQLookup[clip(Qindex + y2ac_delta_q, 127)] * 155 / 100;
|
||||
if (this.y2ac < 8) {
|
||||
this.y2ac = 8;
|
||||
}
|
||||
}
|
||||
|
||||
public void setY2dc(int y2dc_delta_q) {
|
||||
this.y2dc = Globals.vp8DcQLookup[clip(Qindex + y2dc_delta_q, 127)] * 2;
|
||||
}
|
||||
|
||||
public int getFilterStrength() {
|
||||
return filterStrength;
|
||||
}
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Brooss, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.webp.vp8;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
final class SegmentQuants {
|
||||
|
||||
private static DeltaQ get_delta_q(BoolDecoder bc, int prev)
|
||||
throws IOException {
|
||||
DeltaQ ret = new DeltaQ();
|
||||
ret.v = 0;
|
||||
ret.update = false;
|
||||
|
||||
if (bc.readBit() > 0) {
|
||||
ret.v = bc.readLiteral(4);
|
||||
|
||||
if (bc.readBit() > 0) {
|
||||
ret.v = -ret.v;
|
||||
}
|
||||
}
|
||||
|
||||
/* Trigger a quantizer update if the delta-q value has changed */
|
||||
if (ret.v != prev) {
|
||||
ret.update = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private int qIndex;
|
||||
|
||||
private SegmentQuant[] segQuants = new SegmentQuant[Globals.MAX_MB_SEGMENTS];
|
||||
|
||||
public SegmentQuants() {
|
||||
for (int x = 0; x < Globals.MAX_MB_SEGMENTS; x++) {
|
||||
segQuants[x] = new SegmentQuant();
|
||||
}
|
||||
}
|
||||
|
||||
public int getqIndex() {
|
||||
return qIndex;
|
||||
}
|
||||
|
||||
public SegmentQuant[] getSegQuants() {
|
||||
return segQuants;
|
||||
}
|
||||
|
||||
public void parse(BoolDecoder bc, boolean segmentation_enabled,
|
||||
boolean mb_segement_abs_delta) throws IOException {
|
||||
qIndex = bc.readLiteral(7);
|
||||
boolean q_update = false;
|
||||
DeltaQ v = get_delta_q(bc, 0);
|
||||
int y1dc_delta_q = v.v;
|
||||
q_update = q_update || v.update;
|
||||
v = get_delta_q(bc, 0);
|
||||
int y2dc_delta_q = v.v;
|
||||
q_update = q_update || v.update;
|
||||
v = get_delta_q(bc, 0);
|
||||
int y2ac_delta_q = v.v;
|
||||
q_update = q_update || v.update;
|
||||
v = get_delta_q(bc, 0);
|
||||
int uvdc_delta_q = v.v;
|
||||
q_update = q_update || v.update;
|
||||
v = get_delta_q(bc, 0);
|
||||
int uvac_delta_q = v.v;
|
||||
q_update = q_update || v.update;
|
||||
|
||||
for (SegmentQuant s : segQuants) {
|
||||
if (!segmentation_enabled) {
|
||||
s.setQindex(qIndex);
|
||||
} else if (!mb_segement_abs_delta) {
|
||||
s.setQindex(s.getQindex() + qIndex);
|
||||
}
|
||||
|
||||
s.setY1dc(y1dc_delta_q);
|
||||
s.setY2dc(y2dc_delta_q);
|
||||
s.setY2ac_delta_q(y2ac_delta_q);
|
||||
s.setUvdc_delta_q(uvdc_delta_q);
|
||||
s.setUvac_delta_q(uvac_delta_q);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void setSegQuants(SegmentQuant[] segQuants) {
|
||||
this.segQuants = segQuants;
|
||||
}
|
||||
}
|
@ -0,0 +1,629 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Brooss, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.imageio.plugins.webp.vp8;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
final class SubBlock {
|
||||
public enum PLANE {
|
||||
U, V, Y1, Y2
|
||||
}
|
||||
|
||||
public static int planeToType(PLANE plane, Boolean withY2) {
|
||||
switch (plane) {
|
||||
case Y2:
|
||||
return 1;
|
||||
case Y1:
|
||||
if (withY2)
|
||||
return 0;
|
||||
else
|
||||
return 3;
|
||||
case U:
|
||||
return 2;
|
||||
case V:
|
||||
return 2;
|
||||
}
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
private SubBlock above;
|
||||
|
||||
private int[][] dest;
|
||||
private int[][] diff;
|
||||
private boolean hasNoZeroToken;
|
||||
private SubBlock left;
|
||||
private MacroBlock macroBlock;
|
||||
private int mode;
|
||||
private PLANE plane;
|
||||
private int predict[][];
|
||||
private int tokens[];
|
||||
|
||||
SubBlock(MacroBlock macroBlock, SubBlock above, SubBlock left,
|
||||
SubBlock.PLANE plane) {
|
||||
this.macroBlock = macroBlock;
|
||||
this.plane = plane;
|
||||
this.above = above;
|
||||
this.left = left;
|
||||
mode = 0;
|
||||
tokens = new int[16];
|
||||
for (int z = 0; z < 16; z++)
|
||||
tokens[z] = 0;
|
||||
}
|
||||
|
||||
private int DCTextra(BoolDecoder bc2, int p[]) throws IOException {
|
||||
int v = 0;
|
||||
int offset = 0;
|
||||
do {
|
||||
v += v + bc2.readBool(p[offset]);
|
||||
offset++;
|
||||
} while (p[offset] > 0);
|
||||
return v;
|
||||
}
|
||||
|
||||
public void decodeSubBlock(BoolDecoder bc2, int[][][][] coef_probs,
|
||||
int ilc, int type, boolean withY2) throws IOException {
|
||||
SubBlock sb = this;
|
||||
int startAt = 0;
|
||||
if (withY2)
|
||||
startAt = 1;
|
||||
int lc = ilc;
|
||||
int c = 0;
|
||||
int v = 1;
|
||||
|
||||
boolean skip = false;
|
||||
|
||||
while (!(v == Globals.dct_eob) && c + startAt < 16) {
|
||||
|
||||
// if (!skip)
|
||||
// v = bc2.readTree(Globals.vp8CoefTree,
|
||||
// coef_probs[type][Globals.vp8CoefBands[c + startAt]][lc]);
|
||||
// else
|
||||
// v = bc2.readTree(
|
||||
// Globals.vp8CoefTree,
|
||||
// coef_probs[type][Globals.vp8CoefBands[c + startAt]][lc],
|
||||
// 1);
|
||||
v = bc2.readTree(Globals.vp8CoefTree, coef_probs[type][Globals.vp8CoefBands[c + startAt]][lc],
|
||||
skip ? 1 : 0);
|
||||
|
||||
int dv = decodeToken(bc2, v);
|
||||
lc = 0;
|
||||
skip = false;
|
||||
if (dv == 1 || dv == -1)
|
||||
lc = 1;
|
||||
else if (dv > 1 || dv < -1)
|
||||
lc = 2;
|
||||
else if (dv == Globals.DCT_0)
|
||||
skip = true;
|
||||
|
||||
int tokens[] = sb.getTokens();
|
||||
|
||||
if (v != Globals.dct_eob)
|
||||
tokens[Globals.vp8defaultZigZag1d[c + startAt]] = dv;
|
||||
c++;
|
||||
}
|
||||
hasNoZeroToken = false;
|
||||
for (int x = 0; x < 16; x++)
|
||||
if (tokens[x] != 0)
|
||||
hasNoZeroToken = true;
|
||||
}
|
||||
|
||||
private int decodeToken(BoolDecoder bc2, int v) throws IOException {
|
||||
int r = v;
|
||||
|
||||
if (v == Globals.dct_cat1) {
|
||||
r = 5 + DCTextra(bc2, Globals.Pcat1);
|
||||
}
|
||||
if (v == Globals.dct_cat2) {
|
||||
r = 7 + DCTextra(bc2, Globals.Pcat2);
|
||||
}
|
||||
if (v == Globals.dct_cat3) {
|
||||
r = 11 + DCTextra(bc2, Globals.Pcat3);
|
||||
}
|
||||
if (v == Globals.dct_cat4) {
|
||||
r = 19 + DCTextra(bc2, Globals.Pcat4);
|
||||
}
|
||||
if (v == Globals.dct_cat5) {
|
||||
r = 35 + DCTextra(bc2, Globals.Pcat5);
|
||||
}
|
||||
if (v == Globals.dct_cat6) {
|
||||
r = 67 + DCTextra(bc2, Globals.Pcat6);
|
||||
}
|
||||
if (v != Globals.DCT_0 && v != Globals.dct_eob) {
|
||||
if (bc2.readBit() > 0)
|
||||
r = -r;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
public void dequantSubBlock(VP8Frame frame, Integer Dc) {
|
||||
SubBlock sb = this;
|
||||
|
||||
int[] adjustedValues = new int[16];
|
||||
for (int i = 0; i < 16; i++) {
|
||||
int QValue;
|
||||
if (plane == PLANE.U || plane == PLANE.V) {
|
||||
QValue = frame.getSegmentQuants().getSegQuants()[this.getMacroBlock().getSegmentId()]
|
||||
.getUvac_delta_q();
|
||||
if (i == 0)
|
||||
QValue = frame.getSegmentQuants().getSegQuants()[this.getMacroBlock().getSegmentId()]
|
||||
.getUvdc_delta_q();
|
||||
} else {
|
||||
QValue = frame.getSegmentQuants().getSegQuants()[this.getMacroBlock().getSegmentId()].getY1ac();
|
||||
if (i == 0)
|
||||
QValue = frame.getSegmentQuants().getSegQuants()[this.getMacroBlock().getSegmentId()]
|
||||
.getY1dc();
|
||||
}
|
||||
|
||||
int inputValue = sb.getTokens()[i];
|
||||
adjustedValues[i] = inputValue * QValue;
|
||||
|
||||
}
|
||||
|
||||
if (Dc != null)
|
||||
adjustedValues[0] = Dc;
|
||||
|
||||
int[][] diff = IDCT.idct4x4llm(adjustedValues);
|
||||
sb.setDiff(diff);
|
||||
|
||||
}
|
||||
|
||||
public void drawDebug() {
|
||||
if (dest != null) {
|
||||
dest[0][0] = 128;
|
||||
dest[1][0] = 128;
|
||||
dest[2][0] = 128;
|
||||
dest[3][0] = 128;
|
||||
dest[0][0] = 128;
|
||||
dest[0][1] = 128;
|
||||
dest[0][2] = 128;
|
||||
dest[0][3] = 128;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void drawDebugH() {
|
||||
if (dest != null) {
|
||||
dest[0][0] = 0;
|
||||
dest[1][0] = 0;
|
||||
dest[2][0] = 0;
|
||||
dest[3][0] = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void drawDebugV() {
|
||||
if (dest != null) {
|
||||
dest[0][0] = 0;
|
||||
dest[0][1] = 0;
|
||||
dest[0][2] = 0;
|
||||
dest[0][3] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public SubBlock getAbove() {
|
||||
|
||||
return above;
|
||||
}
|
||||
|
||||
public String getDebugString() {
|
||||
String r = new String();
|
||||
r = r + " " + plane;
|
||||
if (getMacroBlock().getYMode() == Globals.B_PRED
|
||||
&& plane == SubBlock.PLANE.Y1)
|
||||
r = r + "\n " + Globals.getSubBlockModeAsString(mode);
|
||||
return r;
|
||||
}
|
||||
|
||||
public int[][] getDest() {
|
||||
if (dest != null)
|
||||
return dest;
|
||||
else
|
||||
return new int[4][4];
|
||||
}
|
||||
|
||||
public int[][] getDiff() {
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
public SubBlock getLeft() {
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
public MacroBlock getMacroBlock() {
|
||||
return macroBlock;
|
||||
}
|
||||
|
||||
public int[][] getMacroBlockPredict(int intra_mode) {
|
||||
if (dest != null)
|
||||
return dest;
|
||||
|
||||
else {
|
||||
int rv = 127;
|
||||
if (intra_mode == Globals.H_PRED)
|
||||
rv = 129;
|
||||
int r[][] = new int[4][4];
|
||||
for (int j = 0; j < 4; j++)
|
||||
for (int i = 0; i < 4; i++)
|
||||
r[i][j] = rv;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
public int getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
public PLANE getPlane() {
|
||||
return plane;
|
||||
}
|
||||
|
||||
public int[][] getPredict() {
|
||||
if (predict != null)
|
||||
return predict;
|
||||
return getPredict(Globals.B_DC_PRED, false);
|
||||
}
|
||||
|
||||
public int[][] getPredict(int intra_bmode, boolean left) {
|
||||
if (dest != null)
|
||||
return dest;
|
||||
if (predict != null)
|
||||
return predict;
|
||||
else {
|
||||
int rv = 127;
|
||||
|
||||
if ((intra_bmode == Globals.B_TM_PRED
|
||||
|| intra_bmode == Globals.B_DC_PRED
|
||||
|| intra_bmode == Globals.B_VE_PRED
|
||||
|| intra_bmode == Globals.B_HE_PRED
|
||||
|| intra_bmode == Globals.B_VR_PRED
|
||||
|| intra_bmode == Globals.B_RD_PRED || intra_bmode == Globals.B_HD_PRED)
|
||||
&& left)
|
||||
|
||||
rv = 129;
|
||||
int r[][] = new int[4][4];
|
||||
for (int j = 0; j < 4; j++)
|
||||
for (int i = 0; i < 4; i++)
|
||||
r[i][j] = rv;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
int[] getTokens() {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
public boolean hasNoZeroToken() {
|
||||
return hasNoZeroToken;
|
||||
}
|
||||
|
||||
public boolean isDest() {
|
||||
if (dest == null)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void predict(VP8Frame frame) {
|
||||
SubBlock sb = this;
|
||||
SubBlock aboveSb = frame.getAboveSubBlock(sb, sb.getPlane());
|
||||
SubBlock leftSb = frame.getLeftSubBlock(sb, sb.getPlane());
|
||||
|
||||
int[] above = new int[4];
|
||||
int[] left = new int[4];
|
||||
|
||||
above[0] = aboveSb.getPredict(sb.getMode(), false)[0][3];
|
||||
above[1] = aboveSb.getPredict(sb.getMode(), false)[1][3];
|
||||
above[2] = aboveSb.getPredict(sb.getMode(), false)[2][3];
|
||||
above[3] = aboveSb.getPredict(sb.getMode(), false)[3][3];
|
||||
left[0] = leftSb.getPredict(sb.getMode(), true)[3][0];
|
||||
left[1] = leftSb.getPredict(sb.getMode(), true)[3][1];
|
||||
left[2] = leftSb.getPredict(sb.getMode(), true)[3][2];
|
||||
left[3] = leftSb.getPredict(sb.getMode(), true)[3][3];
|
||||
SubBlock AL = frame.getLeftSubBlock(aboveSb, sb.getPlane());
|
||||
|
||||
// for above left if left and above is null use left (129?) else use
|
||||
// above (127?)
|
||||
int al;
|
||||
if (!leftSb.isDest() && !aboveSb.isDest()) {
|
||||
|
||||
al = AL.getPredict(sb.getMode(), false)[3][3];
|
||||
} else if (!aboveSb.isDest()) {
|
||||
|
||||
al = AL.getPredict(sb.getMode(), false)[3][3];
|
||||
} else
|
||||
al = AL.getPredict(sb.getMode(), true)[3][3];
|
||||
SubBlock AR = frame.getAboveRightSubBlock(sb, sb.plane);
|
||||
int ar[] = new int[4];
|
||||
ar[0] = AR.getPredict(sb.getMode(), false)[0][3];
|
||||
ar[1] = AR.getPredict(sb.getMode(), false)[1][3];
|
||||
ar[2] = AR.getPredict(sb.getMode(), false)[2][3];
|
||||
ar[3] = AR.getPredict(sb.getMode(), false)[3][3];
|
||||
int[][] p = new int[4][4];
|
||||
int pp[];
|
||||
switch (sb.getMode()) {
|
||||
case Globals.B_DC_PRED:
|
||||
// System.out.println("B_DC_PRED");
|
||||
int expected_dc = 0;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
expected_dc += above[i];
|
||||
expected_dc += left[i];
|
||||
}
|
||||
expected_dc = (expected_dc + 4) >> 3;
|
||||
|
||||
for (int y = 0; y < 4; y++)
|
||||
for (int x = 0; x < 4; x++)
|
||||
p[x][y] = expected_dc;
|
||||
|
||||
break;
|
||||
case Globals.B_TM_PRED:
|
||||
|
||||
// System.out.println("B_TM_PRED");
|
||||
|
||||
// prediction similar to true_motion prediction
|
||||
|
||||
for (int r = 0; r < 4; r++) {
|
||||
for (int c = 0; c < 4; c++) {
|
||||
|
||||
int pred = above[c] - al + left[r];
|
||||
if (pred < 0)
|
||||
pred = 0;
|
||||
|
||||
if (pred > 255)
|
||||
pred = 255;
|
||||
|
||||
p[c][r] = pred;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Globals.B_VE_PRED:
|
||||
// System.out.println("B_VE_PRED");
|
||||
|
||||
int ap[] = new int[4];
|
||||
ap[0] = (al + 2 * above[0] + above[1] + 2) >> 2;
|
||||
ap[1] = (above[0] + 2 * above[1] + above[2] + 2) >> 2;
|
||||
ap[2] = (above[1] + 2 * above[2] + above[3] + 2) >> 2;
|
||||
ap[3] = (above[2] + 2 * above[3] + ar[0] + 2) >> 2;
|
||||
|
||||
for (int r = 0; r < 4; r++) {
|
||||
for (int c = 0; c < 4; c++) {
|
||||
|
||||
p[c][r] = ap[c];
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Globals.B_HE_PRED:
|
||||
// System.out.println("B_HE_PRED");
|
||||
|
||||
int lp[] = new int[4];
|
||||
lp[0] = (al + 2 * left[0] + left[1] + 2) >> 2;
|
||||
lp[1] = (left[0] + 2 * left[1] + left[2] + 2) >> 2;
|
||||
lp[2] = (left[1] + 2 * left[2] + left[3] + 2) >> 2;
|
||||
lp[3] = (left[2] + 2 * left[3] + left[3] + 2) >> 2;
|
||||
|
||||
for (int r = 0; r < 4; r++) {
|
||||
for (int c = 0; c < 4; c++) {
|
||||
p[c][r] = lp[r];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Globals.B_LD_PRED:
|
||||
// System.out.println("B_LD_PRED");
|
||||
p[0][0] = (above[0] + above[1] * 2 + above[2] + 2) >> 2;
|
||||
p[1][0] = p[0][1] = (above[1] + above[2] * 2 + above[3] + 2) >> 2;
|
||||
p[2][0] = p[1][1] = p[0][2] = (above[2] + above[3] * 2 + ar[0] + 2) >> 2;
|
||||
p[3][0] = p[2][1] = p[1][2] = p[0][3] = (above[3] + ar[0] * 2
|
||||
+ ar[1] + 2) >> 2;
|
||||
p[3][1] = p[2][2] = p[1][3] = (ar[0] + ar[1] * 2 + ar[2] + 2) >> 2;
|
||||
p[3][2] = p[2][3] = (ar[1] + ar[2] * 2 + ar[3] + 2) >> 2;
|
||||
p[3][3] = (ar[2] + ar[3] * 2 + ar[3] + 2) >> 2;
|
||||
|
||||
break;
|
||||
case Globals.B_RD_PRED:
|
||||
// System.out.println("B_RD_PRED");
|
||||
pp = new int[9];
|
||||
|
||||
pp[0] = left[3];
|
||||
pp[1] = left[2];
|
||||
pp[2] = left[1];
|
||||
pp[3] = left[0];
|
||||
pp[4] = al;
|
||||
pp[5] = above[0];
|
||||
pp[6] = above[1];
|
||||
pp[7] = above[2];
|
||||
pp[8] = above[3];
|
||||
|
||||
p[0][3] = (pp[0] + pp[1] * 2 + pp[2] + 2) >> 2;
|
||||
p[1][3] = p[0][2] = (pp[1] + pp[2] * 2 + pp[3] + 2) >> 2;
|
||||
p[2][3] = p[1][2] = p[0][1] = (pp[2] + pp[3] * 2 + pp[4] + 2) >> 2;
|
||||
p[3][3] = p[2][2] = p[1][1] = p[0][0] = (pp[3] + pp[4] * 2 + pp[5] + 2) >> 2;
|
||||
p[3][2] = p[2][1] = p[1][0] = (pp[4] + pp[5] * 2 + pp[6] + 2) >> 2;
|
||||
p[3][1] = p[2][0] = (pp[5] + pp[6] * 2 + pp[7] + 2) >> 2;
|
||||
p[3][0] = (pp[6] + pp[7] * 2 + pp[8] + 2) >> 2;
|
||||
break;
|
||||
|
||||
case Globals.B_VR_PRED:
|
||||
// System.out.println("B_VR_PRED");
|
||||
pp = new int[9];
|
||||
|
||||
pp[0] = left[3];
|
||||
pp[1] = left[2];
|
||||
pp[2] = left[1];
|
||||
pp[3] = left[0];
|
||||
pp[4] = al;
|
||||
pp[5] = above[0];
|
||||
pp[6] = above[1];
|
||||
pp[7] = above[2];
|
||||
pp[8] = above[3];
|
||||
|
||||
p[0][3] = (pp[1] + pp[2] * 2 + pp[3] + 2) >> 2;
|
||||
p[0][2] = (pp[2] + pp[3] * 2 + pp[4] + 2) >> 2;
|
||||
p[1][3] = p[0][1] = (pp[3] + pp[4] * 2 + pp[5] + 2) >> 2;
|
||||
p[1][2] = p[0][0] = (pp[4] + pp[5] + 1) >> 1;
|
||||
p[2][3] = p[1][1] = (pp[4] + pp[5] * 2 + pp[6] + 2) >> 2;
|
||||
p[2][2] = p[1][0] = (pp[5] + pp[6] + 1) >> 1;
|
||||
p[3][3] = p[2][1] = (pp[5] + pp[6] * 2 + pp[7] + 2) >> 2;
|
||||
p[3][2] = p[2][0] = (pp[6] + pp[7] + 1) >> 1;
|
||||
p[3][1] = (pp[6] + pp[7] * 2 + pp[8] + 2) >> 2;
|
||||
p[3][0] = (pp[7] + pp[8] + 1) >> 1;
|
||||
|
||||
break;
|
||||
case Globals.B_VL_PRED:
|
||||
// System.out.println("B_VL_PRED");
|
||||
|
||||
p[0][0] = (above[0] + above[1] + 1) >> 1;
|
||||
p[0][1] = (above[0] + above[1] * 2 + above[2] + 2) >> 2;
|
||||
p[0][2] = p[1][0] = (above[1] + above[2] + 1) >> 1;
|
||||
p[1][1] = p[0][3] = (above[1] + above[2] * 2 + above[3] + 2) >> 2;
|
||||
p[1][2] = p[2][0] = (above[2] + above[3] + 1) >> 1;
|
||||
p[1][3] = p[2][1] = (above[2] + above[3] * 2 + ar[0] + 2) >> 2;
|
||||
p[3][0] = p[2][2] = (above[3] + ar[0] + 1) >> 1;
|
||||
p[3][1] = p[2][3] = (above[3] + ar[0] * 2 + ar[1] + 2) >> 2;
|
||||
p[3][2] = (ar[0] + ar[1] * 2 + ar[2] + 2) >> 2;
|
||||
p[3][3] = (ar[1] + ar[2] * 2 + ar[3] + 2) >> 2;
|
||||
|
||||
break;
|
||||
case Globals.B_HD_PRED:
|
||||
// System.out.println("B_HD_PRED");
|
||||
pp = new int[9];
|
||||
pp[0] = left[3];
|
||||
pp[1] = left[2];
|
||||
pp[2] = left[1];
|
||||
pp[3] = left[0];
|
||||
pp[4] = al;
|
||||
pp[5] = above[0];
|
||||
pp[6] = above[1];
|
||||
pp[7] = above[2];
|
||||
pp[8] = above[3];
|
||||
|
||||
p[0][3] = (pp[0] + pp[1] + 1) >> 1;
|
||||
p[1][3] = (pp[0] + pp[1] * 2 + pp[2] + 2) >> 2;
|
||||
p[0][2] = p[2][3] = (pp[1] + pp[2] + 1) >> 1;
|
||||
p[1][2] = p[3][3] = (pp[1] + pp[2] * 2 + pp[3] + 2) >> 2;
|
||||
p[2][2] = p[0][1] = (pp[2] + pp[3] + 1) >> 1;
|
||||
p[3][2] = p[1][1] = (pp[2] + pp[3] * 2 + pp[4] + 2) >> 2;
|
||||
p[2][1] = p[0][0] = (pp[3] + pp[4] + 1) >> 1;
|
||||
p[3][1] = p[1][0] = (pp[3] + pp[4] * 2 + pp[5] + 2) >> 2;
|
||||
p[2][0] = (pp[4] + pp[5] * 2 + pp[6] + 2) >> 2;
|
||||
p[3][0] = (pp[5] + pp[6] * 2 + pp[7] + 2) >> 2;
|
||||
break;
|
||||
case Globals.B_HU_PRED:
|
||||
// System.out.println("B_HU_PRED");
|
||||
|
||||
p[0][0] = (left[0] + left[1] + 1) >> 1;
|
||||
p[1][0] = (left[0] + left[1] * 2 + left[2] + 2) >> 2;
|
||||
p[2][0] = p[0][1] = (left[1] + left[2] + 1) >> 1;
|
||||
p[3][0] = p[1][1] = (left[1] + left[2] * 2 + left[3] + 2) >> 2;
|
||||
p[2][1] = p[0][2] = (left[2] + left[3] + 1) >> 1;
|
||||
p[3][1] = p[1][2] = (left[2] + left[3] * 2 + left[3] + 2) >> 2;
|
||||
p[2][2] = p[3][2] = p[0][3] = p[1][3] = p[2][3] = p[3][3] = left[3];
|
||||
break;
|
||||
|
||||
default:
|
||||
// TODO: FixME!
|
||||
throw new AssertionError("TODO mode: " + sb.getMode());
|
||||
}
|
||||
|
||||
sb.setPredict(p);
|
||||
}
|
||||
|
||||
public void reconstruct() {
|
||||
SubBlock sb = this;
|
||||
|
||||
int r, c;
|
||||
int p[][] = sb.getPredict(1, false);
|
||||
|
||||
int dest[][] = new int[4][4];
|
||||
int diff[][] = sb.getDiff();
|
||||
|
||||
for (r = 0; r < 4; r++) {
|
||||
for (c = 0; c < 4; c++) {
|
||||
int a = diff[r][c] + p[r][c];
|
||||
|
||||
if (a < 0)
|
||||
a = 0;
|
||||
|
||||
if (a > 255)
|
||||
a = 255;
|
||||
|
||||
dest[r][c] = a;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sb.setDest(dest);
|
||||
if (!this.getMacroBlock().isKeepDebugInfo()) {
|
||||
sb.diff = null;
|
||||
sb.predict = null;
|
||||
sb.tokens = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setDest(int[][] dest) {
|
||||
this.dest = dest;
|
||||
}
|
||||
|
||||
public void setDiff(int[][] diff) {
|
||||
this.diff = diff;
|
||||
}
|
||||
|
||||
public void setMode(int mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
public void setPixel(int x, int y, int p) {
|
||||
if (dest == null) {
|
||||
dest = new int[4][4];
|
||||
}
|
||||
dest[x][y] = p;
|
||||
}
|
||||
|
||||
public void setPredict(int[][] predict) {
|
||||
this.predict = predict;
|
||||
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
String r = "[";
|
||||
for (int x = 0; x < 16; x++)
|
||||
r = r + tokens[x] + " ";
|
||||
r = r + "]";
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1 @@
|
||||
com.twelvemonkeys.imageio.plugins.webp.WebPImageReaderSpi
|
@ -0,0 +1,317 @@
|
||||
package com.twelvemonkeys.imageio.plugins.webp;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.function.ThrowingRunnable;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
import javax.imageio.metadata.IIOMetadataFormatImpl;
|
||||
import javax.imageio.metadata.IIOMetadataNode;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* WebPImageMetadataTest.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: WebPImageMetadataTest.java,v 1.0 21/11/2020 haraldk Exp$
|
||||
*/
|
||||
public class WebPImageMetadataTest {
|
||||
@Test
|
||||
public void testStandardFeatures() {
|
||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8_, 27, 33);
|
||||
final WebPImageMetadata metadata = new WebPImageMetadata(header);
|
||||
|
||||
// Standard metadata format
|
||||
assertTrue(metadata.isStandardMetadataFormatSupported());
|
||||
Node root = metadata.getAsTree(IIOMetadataFormatImpl.standardMetadataFormatName);
|
||||
assertNotNull(root);
|
||||
assertTrue(root instanceof IIOMetadataNode);
|
||||
|
||||
// Other formats
|
||||
assertNull(metadata.getNativeMetadataFormatName());
|
||||
assertNull(metadata.getExtraMetadataFormatNames());
|
||||
assertThrows(IllegalArgumentException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
metadata.getAsTree("com_foo_bar_1.0");
|
||||
}
|
||||
});
|
||||
|
||||
// Read-only
|
||||
assertTrue(metadata.isReadOnly());
|
||||
assertThrows(IllegalStateException.class, new ThrowingRunnable() {
|
||||
@Override
|
||||
public void run() throws Throwable {
|
||||
metadata.mergeTree(IIOMetadataFormatImpl.standardMetadataFormatName, new IIOMetadataNode(IIOMetadataFormatImpl.standardMetadataFormatName));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardChromaRGB() {
|
||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8_, 27, 33);
|
||||
WebPImageMetadata metadata = new WebPImageMetadata(header);
|
||||
|
||||
IIOMetadataNode chroma = metadata.getStandardChromaNode();
|
||||
assertNotNull(chroma);
|
||||
assertEquals("Chroma", chroma.getNodeName());
|
||||
assertEquals(3, chroma.getLength());
|
||||
|
||||
IIOMetadataNode colorSpaceType = (IIOMetadataNode) chroma.getFirstChild();
|
||||
assertEquals("ColorSpaceType", colorSpaceType.getNodeName());
|
||||
assertEquals("RGB", colorSpaceType.getAttribute("name"));
|
||||
|
||||
IIOMetadataNode numChannels = (IIOMetadataNode) colorSpaceType.getNextSibling();
|
||||
assertEquals("NumChannels", numChannels.getNodeName());
|
||||
assertEquals("3", numChannels.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode blackIsZero = (IIOMetadataNode) numChannels.getNextSibling();
|
||||
assertEquals("BlackIsZero", blackIsZero.getNodeName());
|
||||
assertEquals("TRUE", blackIsZero.getAttribute("value"));
|
||||
|
||||
assertNull(blackIsZero.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardChromaRGBA() {
|
||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
|
||||
header.containsALPH = true;
|
||||
WebPImageMetadata metadata = new WebPImageMetadata(header);
|
||||
|
||||
IIOMetadataNode chroma = metadata.getStandardChromaNode();
|
||||
assertNotNull(chroma);
|
||||
assertEquals("Chroma", chroma.getNodeName());
|
||||
assertEquals(3, chroma.getLength());
|
||||
|
||||
IIOMetadataNode colorSpaceType = (IIOMetadataNode) chroma.getFirstChild();
|
||||
assertEquals("ColorSpaceType", colorSpaceType.getNodeName());
|
||||
assertEquals("RGB", colorSpaceType.getAttribute("name"));
|
||||
|
||||
IIOMetadataNode numChannels = (IIOMetadataNode) colorSpaceType.getNextSibling();
|
||||
assertEquals("NumChannels", numChannels.getNodeName());
|
||||
assertEquals("4", numChannels.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode blackIsZero = (IIOMetadataNode) numChannels.getNextSibling();
|
||||
assertEquals("BlackIsZero", blackIsZero.getNodeName());
|
||||
assertEquals("TRUE", blackIsZero.getAttribute("value"));
|
||||
|
||||
assertNull(blackIsZero.getNextSibling());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardCompressionVP8() {
|
||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8_, 27, 33);
|
||||
WebPImageMetadata metadata = new WebPImageMetadata(header);
|
||||
|
||||
IIOMetadataNode compression = metadata.getStandardCompressionNode();
|
||||
assertNotNull(compression);
|
||||
assertEquals("Compression", compression.getNodeName());
|
||||
assertEquals(2, compression.getLength());
|
||||
|
||||
IIOMetadataNode compressionTypeName = (IIOMetadataNode) compression.getFirstChild();
|
||||
assertEquals("CompressionTypeName", compressionTypeName.getNodeName());
|
||||
assertEquals("VP8", compressionTypeName.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode lossless = (IIOMetadataNode) compressionTypeName.getNextSibling();
|
||||
assertEquals("Lossless", lossless.getNodeName());
|
||||
assertEquals("FALSE", lossless.getAttribute("value"));
|
||||
|
||||
assertNull(lossless.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardCompressionVP8L() {
|
||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8L, 27, 33);
|
||||
header.isLossless = true;
|
||||
WebPImageMetadata metadata = new WebPImageMetadata(header);
|
||||
|
||||
IIOMetadataNode compression = metadata.getStandardCompressionNode();
|
||||
assertNotNull(compression);
|
||||
assertEquals("Compression", compression.getNodeName());
|
||||
assertEquals(2, compression.getLength());
|
||||
|
||||
IIOMetadataNode compressionTypeName = (IIOMetadataNode) compression.getFirstChild();
|
||||
assertEquals("CompressionTypeName", compressionTypeName.getNodeName());
|
||||
assertEquals("VP8L", compressionTypeName.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode lossless = (IIOMetadataNode) compressionTypeName.getNextSibling();
|
||||
assertEquals("Lossless", lossless.getNodeName());
|
||||
assertEquals("TRUE", lossless.getAttribute("value"));
|
||||
|
||||
assertNull(lossless.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardCompressionVP8X() {
|
||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
|
||||
WebPImageMetadata metadata = new WebPImageMetadata(header);
|
||||
|
||||
IIOMetadataNode compression = metadata.getStandardCompressionNode();
|
||||
assertNotNull(compression);
|
||||
assertEquals("Compression", compression.getNodeName());
|
||||
assertEquals(2, compression.getLength());
|
||||
|
||||
IIOMetadataNode compressionTypeName = (IIOMetadataNode) compression.getFirstChild();
|
||||
assertEquals("CompressionTypeName", compressionTypeName.getNodeName());
|
||||
assertEquals("VP8", compressionTypeName.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode lossless = (IIOMetadataNode) compressionTypeName.getNextSibling();
|
||||
assertEquals("Lossless", lossless.getNodeName());
|
||||
assertEquals("FALSE", lossless.getAttribute("value"));
|
||||
|
||||
assertNull(lossless.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardCompressionVP8XLossless() {
|
||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
|
||||
header.isLossless = true;
|
||||
WebPImageMetadata metadata = new WebPImageMetadata(header);
|
||||
|
||||
IIOMetadataNode compression = metadata.getStandardCompressionNode();
|
||||
assertNotNull(compression);
|
||||
assertEquals("Compression", compression.getNodeName());
|
||||
assertEquals(2, compression.getLength());
|
||||
|
||||
IIOMetadataNode compressionTypeName = (IIOMetadataNode) compression.getFirstChild();
|
||||
assertEquals("CompressionTypeName", compressionTypeName.getNodeName());
|
||||
assertEquals("VP8L", compressionTypeName.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode lossless = (IIOMetadataNode) compressionTypeName.getNextSibling();
|
||||
assertEquals("Lossless", lossless.getNodeName());
|
||||
assertEquals("TRUE", lossless.getAttribute("value"));
|
||||
|
||||
assertNull(lossless.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDataRGB() {
|
||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8_, 27, 33);
|
||||
WebPImageMetadata metadata = new WebPImageMetadata(header);
|
||||
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
assertEquals("Data", data.getNodeName());
|
||||
assertEquals(3, data.getLength());
|
||||
|
||||
IIOMetadataNode planarConfiguration = (IIOMetadataNode) data.getFirstChild();
|
||||
assertEquals("PlanarConfiguration", planarConfiguration.getNodeName());
|
||||
assertEquals("PixelInterleaved", planarConfiguration.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode sampleFomat = (IIOMetadataNode) planarConfiguration.getNextSibling();
|
||||
assertEquals("SampleFormat", sampleFomat.getNodeName());
|
||||
assertEquals("UnsignedIntegral", sampleFomat.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode bitsPerSample = (IIOMetadataNode) sampleFomat.getNextSibling();
|
||||
assertEquals("BitsPerSample", bitsPerSample.getNodeName());
|
||||
assertEquals("8 8 8", bitsPerSample.getAttribute("value"));
|
||||
|
||||
assertNull(bitsPerSample.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDataRGBA() {
|
||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
|
||||
header.containsALPH = true;
|
||||
WebPImageMetadata metadata = new WebPImageMetadata(header);
|
||||
|
||||
IIOMetadataNode data = metadata.getStandardDataNode();
|
||||
assertNotNull(data);
|
||||
assertEquals("Data", data.getNodeName());
|
||||
assertEquals(3, data.getLength());
|
||||
|
||||
IIOMetadataNode planarConfiguration = (IIOMetadataNode) data.getFirstChild();
|
||||
assertEquals("PlanarConfiguration", planarConfiguration.getNodeName());
|
||||
assertEquals("PixelInterleaved", planarConfiguration.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode sampleFomat = (IIOMetadataNode) planarConfiguration.getNextSibling();
|
||||
assertEquals("SampleFormat", sampleFomat.getNodeName());
|
||||
assertEquals("UnsignedIntegral", sampleFomat.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode bitsPerSample = (IIOMetadataNode) sampleFomat.getNextSibling();
|
||||
assertEquals("BitsPerSample", bitsPerSample.getNodeName());
|
||||
assertEquals("8 8 8 8", bitsPerSample.getAttribute("value"));
|
||||
|
||||
assertNull(bitsPerSample.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDimensionNormal() {
|
||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
|
||||
WebPImageMetadata metadata = new WebPImageMetadata(header);
|
||||
|
||||
IIOMetadataNode dimension = metadata.getStandardDimensionNode();
|
||||
assertNotNull(dimension);
|
||||
assertEquals("Dimension", dimension.getNodeName());
|
||||
assertEquals(2, dimension.getLength());
|
||||
|
||||
IIOMetadataNode imageOrientation = (IIOMetadataNode) dimension.getFirstChild();
|
||||
assertEquals("ImageOrientation", imageOrientation.getNodeName());
|
||||
assertEquals("Normal", imageOrientation.getAttribute("value"));
|
||||
|
||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) imageOrientation.getNextSibling();
|
||||
assertEquals("PixelAspectRatio", pixelAspectRatio.getNodeName());
|
||||
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
|
||||
|
||||
assertNull(pixelAspectRatio.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardDocument() {
|
||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
|
||||
WebPImageMetadata metadata = new WebPImageMetadata(header);
|
||||
|
||||
IIOMetadataNode document = metadata.getStandardDocumentNode();
|
||||
assertNotNull(document);
|
||||
assertEquals("Document", document.getNodeName());
|
||||
assertEquals(1, document.getLength());
|
||||
|
||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) document.getFirstChild();
|
||||
assertEquals("FormatVersion", pixelAspectRatio.getNodeName());
|
||||
assertEquals("1.0", pixelAspectRatio.getAttribute("value"));
|
||||
|
||||
assertNull(pixelAspectRatio.getNextSibling()); // No more children
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardText() {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardTransparencyVP8() {
|
||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
|
||||
WebPImageMetadata metadata = new WebPImageMetadata(header);
|
||||
|
||||
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
|
||||
assertNull(transparency); // No transparency, just defaults
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardTransparencyVP8L() {
|
||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
|
||||
WebPImageMetadata metadata = new WebPImageMetadata(header);
|
||||
|
||||
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
|
||||
assertNull(transparency); // No transparency, just defaults
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStandardTransparencyVP8X() {
|
||||
VP8xChunk header = new VP8xChunk(WebP.CHUNK_VP8X, 27, 33);
|
||||
header.containsALPH = true;
|
||||
WebPImageMetadata metadata = new WebPImageMetadata(header);
|
||||
|
||||
IIOMetadataNode transparency = metadata.getStandardTransparencyNode();
|
||||
assertNotNull(transparency);
|
||||
assertEquals("Transparency", transparency.getNodeName());
|
||||
assertEquals(1, transparency.getLength());
|
||||
|
||||
IIOMetadataNode pixelAspectRatio = (IIOMetadataNode) transparency.getFirstChild();
|
||||
assertEquals("Alpha", pixelAspectRatio.getNodeName());
|
||||
assertEquals("nonpremultiplied", pixelAspectRatio.getAttribute("value"));
|
||||
|
||||
assertNull(pixelAspectRatio.getNextSibling()); // No more children
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package com.twelvemonkeys.imageio.plugins.webp;
|
||||
|
||||
import com.twelvemonkeys.imageio.util.ImageReaderAbstractTest;
|
||||
|
||||
import javax.imageio.spi.ImageReaderSpi;
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
/**
|
||||
* WebPImageReaderTest
|
||||
*/
|
||||
public class WebPImageReaderTest extends ImageReaderAbstractTest<WebPImageReader> {
|
||||
|
||||
@Override
|
||||
protected List<TestData> getTestData() {
|
||||
return asList(
|
||||
// Original Google WebP sample files
|
||||
new TestData(getClassLoaderResource("/webp/1.webp"), new Dimension(550, 368)),
|
||||
new TestData(getClassLoaderResource("/webp/5.webp"), new Dimension(1024, 752)),
|
||||
// Various samples from javavp8codec project
|
||||
new TestData(getClassLoaderResource("/webp/bug3.webp"), new Dimension(95, 95)),
|
||||
new TestData(getClassLoaderResource("/webp/segment01.webp"), new Dimension(160, 160)),
|
||||
new TestData(getClassLoaderResource("/webp/segment02.webp"), new Dimension(160, 160)),
|
||||
new TestData(getClassLoaderResource("/webp/segment03.webp"), new Dimension(160, 160)),
|
||||
new TestData(getClassLoaderResource("/webp/small_1x1.webp"), new Dimension(1, 1)),
|
||||
new TestData(getClassLoaderResource("/webp/small_1x13.webp"), new Dimension(1, 13)),
|
||||
new TestData(getClassLoaderResource("/webp/small_13x1.webp"), new Dimension(13, 1)),
|
||||
new TestData(getClassLoaderResource("/webp/small_31x13.webp"), new Dimension(31, 13)),
|
||||
new TestData(getClassLoaderResource("/webp/test.webp"), new Dimension(128, 128)),
|
||||
new TestData(getClassLoaderResource("/webp/very_short.webp"), new Dimension(63, 66)),
|
||||
// Lossless
|
||||
new TestData(getClassLoaderResource("/webp/1_webp_ll.webp"), new Dimension(400, 301)),
|
||||
// Extended format: Alpha + VP8
|
||||
new TestData(getClassLoaderResource("/webp/1_webp_a.webp"), new Dimension(400, 301)),
|
||||
// Extendad format: Anim
|
||||
new TestData(getClassLoaderResource("/webp/animated-webp-supported.webp"), new Dimension(400, 400))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImageReaderSpi createProvider() {
|
||||
return new WebPImageReaderSpi();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getFormatNames() {
|
||||
return asList("webp", "WEBP");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getSuffixes() {
|
||||
return asList("wbp", "webp");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getMIMETypes() {
|
||||
return asList("image/webp", "image/x-webp");
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.twelvemonkeys.imageio.plugins.webp;
|
||||
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfo;
|
||||
import com.twelvemonkeys.imageio.spi.ReaderWriterProviderInfoTest;
|
||||
|
||||
/**
|
||||
* WebPProviderInfoTest.
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: WebPProviderInfoTest.java,v 1.0 21/11/2020 haraldk Exp$
|
||||
*/
|
||||
public class WebPProviderInfoTest extends ReaderWriterProviderInfoTest {
|
||||
@Override
|
||||
protected ReaderWriterProviderInfo createProviderInfo() {
|
||||
return new WebPProviderInfo();
|
||||
}
|
||||
}
|
BIN
imageio/imageio-webp/src/test/resources/webp/1.webp
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
imageio/imageio-webp/src/test/resources/webp/1_webp_a.webp
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
imageio/imageio-webp/src/test/resources/webp/1_webp_ll.webp
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
imageio/imageio-webp/src/test/resources/webp/5.webp
Normal file
After Width: | Height: | Size: 81 KiB |
After Width: | Height: | Size: 36 KiB |
BIN
imageio/imageio-webp/src/test/resources/webp/bug3.webp
Normal file
After Width: | Height: | Size: 954 B |
BIN
imageio/imageio-webp/src/test/resources/webp/segment01.webp
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
imageio/imageio-webp/src/test/resources/webp/segment02.webp
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
imageio/imageio-webp/src/test/resources/webp/segment03.webp
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
imageio/imageio-webp/src/test/resources/webp/small_13x1.webp
Normal file
After Width: | Height: | Size: 106 B |
BIN
imageio/imageio-webp/src/test/resources/webp/small_1x1.webp
Normal file
After Width: | Height: | Size: 94 B |
BIN
imageio/imageio-webp/src/test/resources/webp/small_1x13.webp
Normal file
After Width: | Height: | Size: 106 B |
BIN
imageio/imageio-webp/src/test/resources/webp/small_31x13.webp
Normal file
After Width: | Height: | Size: 262 B |
BIN
imageio/imageio-webp/src/test/resources/webp/test.webp
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
imageio/imageio-webp/src/test/resources/webp/very_short.webp
Normal file
After Width: | Height: | Size: 86 B |
@ -45,6 +45,7 @@
|
||||
<module>imageio-tga</module>
|
||||
<module>imageio-thumbsdb</module>
|
||||
<module>imageio-tiff</module>
|
||||
<module>imageio-webp</module>
|
||||
<module>imageio-xwd</module>
|
||||
|
||||
<!-- Wrappers for 3rd party libs -->
|
||||
|