You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by da...@apache.org on 2011/04/10 11:08:42 UTC
svn commit: r1090754 - in /commons/proper/sanselan/trunk/src:
main/java/org/apache/sanselan/formats/jpeg/
main/java/org/apache/sanselan/formats/jpeg/decoder/
main/java/org/apache/sanselan/formats/jpeg/segments/
test/java/org/apache/sanselan/roundtrip/
Author: damjan
Date: Sun Apr 10 09:08:42 2011
New Revision: 1090754
URL: http://svn.apache.org/viewvc?rev=1090754&view=rev
Log:
Fix some JPEG marker names, improve JPEG parsing,
add support for reading baseline JPEG images,
and enable JPEG round-trip read tests.
Added:
commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/Block.java (with props)
commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/ZigZag.java (with props)
commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/decoder/
commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/decoder/DCT.java (with props)
commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/decoder/JpegDecoder.java (with props)
commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/decoder/JpegInputStream.java (with props)
commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/decoder/YCbCrConverter.java (with props)
commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/DHTSegment.java (with props)
commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/DQTSegment.java (with props)
Modified:
commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegConstants.java
commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegImageParser.java
commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegUtils.java
commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/SOFNSegment.java
commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/SOSSegment.java
commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/roundtrip/RoundtripTest.java
Added: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/Block.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/Block.java?rev=1090754&view=auto
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/Block.java (added)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/Block.java Sun Apr 10 09:08:42 2011
@@ -0,0 +1,30 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * under the License.
+ */
+
+package org.apache.sanselan.formats.jpeg;
+
+public final class Block
+{
+ public final int[] samples;
+ public final int width;
+ public final int height;
+
+ public Block(int width, int height)
+ {
+ samples = new int[width*height];
+ this.width = width;
+ this.height = height;
+ }
+}
Propchange: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/Block.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegConstants.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegConstants.java?rev=1090754&r1=1090753&r2=1090754&view=diff
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegConstants.java (original)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegConstants.java Sun Apr 10 09:08:42 2011
@@ -77,8 +77,6 @@ public interface JpegConstants
public static final byte SOI[] = new byte[] { (byte) 0xff, (byte) 0xd8 };
public static final byte EOI[] = new byte[] { (byte) 0xff, (byte) 0xd9 };
- public static final int SOS_Marker = (0xff00) | (0xda);
-
public static final int JPEG_APP0 = 0xE0;
// public static final int JPEG_APP1 = JPEG_APP0 + 1;
// public static final int JPEG_APP1_Marker = (0xff00) | JPEG_APP1;
@@ -95,7 +93,7 @@ public interface JpegConstants
public static final int SOF1Marker = 0xFFc0 + 0x1;
public static final int SOF2Marker = 0xFFc0 + 0x2;
public static final int SOF3Marker = 0xFFc0 + 0x3;
- public static final int SOF4Marker = 0xFFc0 + 0x4;
+ public static final int DHTMarker = 0xFFc0 + 0x4;
public static final int SOF5Marker = 0xFFc0 + 0x5;
public static final int SOF6Marker = 0xFFc0 + 0x6;
public static final int SOF7Marker = 0xFFc0 + 0x7;
@@ -103,18 +101,24 @@ public interface JpegConstants
public static final int SOF9Marker = 0xFFc0 + 0x9;
public static final int SOF10Marker = 0xFFc0 + 0xa;
public static final int SOF11Marker = 0xFFc0 + 0xb;
- public static final int SOF12Marker = 0xFFc0 + 0xc;
+ public static final int DACMarker = 0xFFc0 + 0xc;
public static final int SOF13Marker = 0xFFc0 + 0xd;
public static final int SOF14Marker = 0xFFc0 + 0xe;
public static final int SOF15Marker = 0xFFc0 + 0xf;
- public static final int MARKERS[] = { SOS_Marker, JPEG_APP0,
+ public static final int EOIMarker = 0xFFd9;
+ public static final int SOS_Marker = 0xFFda;
+ public static final int DQTMarker = 0xFFdb;
+ public static final int DNLMarker = 0xFFdc;
+
+ public static final int MARKERS[] = { JPEG_APP0,
JPEG_APP0_Marker, JPEG_APP1_Marker, JPEG_APP2_Marker,
JPEG_APP13_Marker, JPEG_APP14_Marker, JPEG_APP15_Marker,
JFIFMarker, SOF0Marker, SOF1Marker, SOF2Marker, SOF3Marker,
- SOF4Marker, SOF5Marker, SOF6Marker, SOF7Marker, SOF8Marker,
- SOF9Marker, SOF10Marker, SOF11Marker, SOF12Marker, SOF13Marker,
- SOF14Marker, SOF15Marker, };
+ DHTMarker, SOF5Marker, SOF6Marker, SOF7Marker, SOF8Marker,
+ SOF9Marker, SOF10Marker, SOF11Marker, DACMarker, SOF13Marker,
+ SOF14Marker, SOF15Marker, EOIMarker, SOS_Marker,
+ DQTMarker, DNLMarker };
public static final byte icc_profile_label[] = { 0x49, 0x43, 0x43, 0x5F,
0x50, 0x52, 0x4F, 0x46, 0x49, 0x4C, 0x45, 0x0 };
Modified: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegImageParser.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegImageParser.java?rev=1090754&r1=1090753&r2=1090754&view=diff
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegImageParser.java (original)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegImageParser.java Sun Apr 10 09:08:42 2011
@@ -23,6 +23,7 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.text.NumberFormat;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -34,10 +35,12 @@ import org.apache.sanselan.ImageParser;
import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.common.IImageMetadata;
import org.apache.sanselan.common.byteSources.ByteSource;
+import org.apache.sanselan.formats.jpeg.decoder.JpegDecoder;
import org.apache.sanselan.formats.jpeg.iptc.IPTCParser;
import org.apache.sanselan.formats.jpeg.iptc.PhotoshopApp13Data;
import org.apache.sanselan.formats.jpeg.segments.App13Segment;
import org.apache.sanselan.formats.jpeg.segments.App2Segment;
+import org.apache.sanselan.formats.jpeg.segments.DQTSegment;
import org.apache.sanselan.formats.jpeg.segments.GenericSegment;
import org.apache.sanselan.formats.jpeg.segments.JFIFSegment;
import org.apache.sanselan.formats.jpeg.segments.SOFNSegment;
@@ -87,8 +90,8 @@ public class JpegImageParser extends Ima
public final BufferedImage getBufferedImage(ByteSource byteSource,
Map params) throws ImageReadException, IOException
{
- throw new ImageReadException(
- "Sanselan cannot read or write JPEG images.");
+ JpegDecoder jpegDecoder = new JpegDecoder();
+ return jpegDecoder.decode(byteSource);
}
private boolean keepMarker(int marker, int markers[])
@@ -111,6 +114,14 @@ public class JpegImageParser extends Ima
{
final ArrayList result = new ArrayList();
final JpegImageParser parser = this;
+ final int[] sofnSegments = {
+ // kJFIFMarker,
+ SOF0Marker,
+
+ SOF1Marker, SOF2Marker, SOF3Marker, SOF5Marker, SOF6Marker,
+ SOF7Marker, SOF9Marker, SOF10Marker, SOF11Marker, SOF13Marker,
+ SOF14Marker, SOF15Marker,
+ };
JpegUtils.Visitor visitor = new JpegUtils.Visitor() {
// return false to exit before reading image data.
@@ -129,7 +140,7 @@ public class JpegImageParser extends Ima
int markerLength, byte markerLengthBytes[],
byte segmentData[]) throws ImageReadException, IOException
{
- if (marker == 0xffd9)
+ if (marker == EOIMarker)
return false;
// Debug.debug("visitSegment marker", marker);
@@ -151,9 +162,12 @@ public class JpegImageParser extends Ima
} else if (marker == JFIFMarker)
{
result.add(new JFIFSegment(marker, segmentData));
- } else if ((marker >= SOF0Marker) && (marker <= SOF15Marker))
+ } else if (Arrays.binarySearch(sofnSegments, marker) >= 0)
{
result.add(new SOFNSegment(marker, segmentData));
+ } else if (marker == DQTMarker)
+ {
+ result.add(new DQTSegment(marker, segmentData));
} else if ((marker >= JPEG_APP1_Marker)
&& (marker <= JPEG_APP15_Marker))
{
Modified: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegUtils.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegUtils.java?rev=1090754&r1=1090753&r2=1090754&view=diff
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegUtils.java (original)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/JpegUtils.java Sun Apr 10 09:08:42 2011
@@ -73,7 +73,7 @@ public class JpegUtils extends BinaryFil
// Debug.debug("marker", marker + " (0x" + Integer.toHexString(marker) + ")");
// Debug.debug("markerBytes", markerBytes);
- if (marker == 0xffd9 || marker == SOS_Marker)
+ if (marker == EOIMarker || marker == SOS_Marker)
{
if (!visitor.beginSOS())
return;
@@ -146,7 +146,7 @@ public class JpegUtils extends BinaryFil
return "SOF2Marker";
case SOF3Marker:
return "SOF3Marker";
- case SOF4Marker:
+ case DHTMarker:
return "SOF4Marker";
case SOF5Marker:
return "SOF5Marker";
@@ -162,14 +162,16 @@ public class JpegUtils extends BinaryFil
return "SOF10Marker";
case SOF11Marker:
return "SOF11Marker";
- case SOF12Marker:
- return "SOF12Marker";
+ case DACMarker:
+ return "DACMarker";
case SOF13Marker:
return "SOF13Marker";
case SOF14Marker:
return "SOF14Marker";
case SOF15Marker:
return "SOF15Marker";
+ case DQTMarker:
+ return "DQTMarker";
default:
return "Unknown";
}
Added: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/ZigZag.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/ZigZag.java?rev=1090754&view=auto
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/ZigZag.java (added)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/ZigZag.java Sun Apr 10 09:08:42 2011
@@ -0,0 +1,47 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * under the License.
+ */
+
+package org.apache.sanselan.formats.jpeg;
+
+public class ZigZag
+{
+ private static final int zigZag[] =
+ {
+ 0, 1, 5, 6, 14, 15, 27, 28,
+ 2, 4, 7, 13, 16, 26, 29, 42,
+ 3, 8, 12, 17, 25, 30, 41, 43,
+ 9, 11, 18, 24, 31, 40, 44, 53,
+ 10, 19, 23, 32, 39, 45, 52, 54,
+ 20, 22, 33, 38, 46, 51, 55, 60,
+ 21, 34, 37, 47, 50, 56, 59, 61,
+ 35, 36, 48, 49, 57, 58, 62, 63
+ };
+
+ public static void zigZagToBlock(int[] zz, int[] block)
+ {
+ for (int i = 0; i < 64; i++)
+ {
+ block[i] = zz[zigZag[i]];
+ }
+ }
+
+ public static void blockToZigZag(int[] block, int[] zz)
+ {
+ for (int i = 0; i < 64; i++)
+ {
+ zz[zigZag[i]] = block[i];
+ }
+ }
+}
Propchange: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/ZigZag.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/decoder/DCT.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/decoder/DCT.java?rev=1090754&view=auto
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/decoder/DCT.java (added)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/decoder/DCT.java Sun Apr 10 09:08:42 2011
@@ -0,0 +1,594 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * under the License.
+ */
+
+package org.apache.sanselan.formats.jpeg.decoder;
+
+public class DCT
+{
+ /*
+ * The book "JPEG still image data compression standard",
+ * by Pennebaker and Mitchell, Chapter 4, discusses a number of
+ * approaches to the fast DCT. Here's the cost, exluding modified
+ * (de)quantization, for transforming an 8x8 block:
+ *
+ * Algorithm Adds Multiplies RightShifts Total
+ * Naive 896 1024 0 1920
+ * "Symmetries" 448 224 0 672
+ * Vetterli and 464 208 0 672
+ * Ligtenberg
+ * Arai, Agui and 464 80 0 544
+ * Nakajima (AA&N)
+ * Feig 8x8 462 54 6 522
+ * Fused mul/add 416
+ * (a pipe dream)
+ *
+ * IJG's libjpeg, FFmpeg, and a number of others use AA&N.
+ *
+ * It would appear that Feig does 4-5% less operations, and
+ * multiplications are reduced from 80 in AA&N to only 54.
+ * But in practice:
+ *
+ * Benchmarks, Intel Core i3 @ 2.93 GHz in long mode, 4 GB RAM
+ * Time taken to do 100 million IDCTs (less is better):
+ * Rene' Stöckel's Feig, int: 45.07 seconds
+ * My Feig, floating point: 36.252 seconds
+ * AA&N, unrolled loops, double[][] -> double[][]: 25.167 seconds
+ *
+ * Clearly Feig is hopeless. I suspect the performance killer is simply
+ * the weight of the algorithm: massive number of local variables,
+ * large code size, and lots of random array accesses.
+ *
+ * Also, AA&N can be optimized a lot:
+ * AA&N, rolled loops, double[][] -> double[][]: 21.162 seconds
+ * AA&N, rolled loops, float[][] -> float[][]: no improvement, but
+ * at some stage Hotspot might start doing SIMD, so let's use float
+ * AA&N, rolled loops, float[] -> float[][]: 19.979 seconds
+ * apparently 2D arrays are slow!
+ * AA&N, rolled loops, inlined 1D AA&N transform, float[]
+ * transformed in-place: 18.5 seconds
+ * AA&N, previous version rewritten in C and compiled with "gcc -O3"
+ * takes: 8.5 seconds (probably due to heavy use of SIMD)
+ *
+ * Other brave attempts:
+ * AA&N, best float version converted to 16:16 fixed point: 23.923 seconds
+ *
+ * Anyway the best float version stays.
+ * 18.5 seconds = 5.4 million transforms per second per core :-)
+ */
+
+ private static final float[] dctScalingFactors =
+ {
+ (float)(0.5 / Math.sqrt(2.0)),
+ (float)(0.25 / Math.cos(Math.PI/16.0)),
+ (float)(0.25 / Math.cos(2.0*Math.PI/16.0)),
+ (float)(0.25 / Math.cos(3.0*Math.PI/16.0)),
+ (float)(0.25 / Math.cos(4.0*Math.PI/16.0)),
+ (float)(0.25 / Math.cos(5.0*Math.PI/16.0)),
+ (float)(0.25 / Math.cos(6.0*Math.PI/16.0)),
+ (float)(0.25 / Math.cos(7.0*Math.PI/16.0)),
+ };
+
+ private static final float[] idctScalingFactors =
+ {
+ (float)(2.0 * 4.0 / Math.sqrt(2.0) * 0.0625),
+ (float)(4.0 * Math.cos(Math.PI/16.0) * 0.125),
+ (float)(4.0 * Math.cos(2.0*Math.PI/16.0) * 0.125),
+ (float)(4.0 * Math.cos(3.0*Math.PI/16.0) * 0.125),
+ (float)(4.0 * Math.cos(4.0*Math.PI/16.0) * 0.125),
+ (float)(4.0 * Math.cos(5.0*Math.PI/16.0) * 0.125),
+ (float)(4.0 * Math.cos(6.0*Math.PI/16.0) * 0.125),
+ (float)(4.0 * Math.cos(7.0*Math.PI/16.0) * 0.125),
+ };
+
+ private static final float A1 = (float)(Math.cos(2.0*Math.PI/8.0));
+ private static final float A2 = (float)(Math.cos(Math.PI/8.0) - Math.cos(3.0*Math.PI/8.0));
+ private static final float A3 = A1;
+ private static final float A4 = (float)(Math.cos(Math.PI/8.0) + Math.cos(3.0*Math.PI/8.0));
+ private static final float A5 = (float)(Math.cos(3.0*Math.PI/8.0));
+
+ private static final float C2 = (float)(2.0 * Math.cos(Math.PI/8));
+ private static final float C4 = (float)(2.0 * Math.cos(2*Math.PI/8));
+ private static final float C6 = (float)(2.0 * Math.cos(3*Math.PI/8));
+ private static final float Q = C2 - C6;
+ private static final float R = C2 + C6;
+
+ public static void scaleQuantizationVector(float[] vector)
+ {
+ for (int x = 0; x < 8; x++)
+ vector[x] *= dctScalingFactors[x];
+ }
+
+ public static void scaleDequantizationVector(float[] vector)
+ {
+ for (int x = 0; x < 8; x++)
+ vector[x] *= idctScalingFactors[x];
+ }
+
+ public static void scaleQuantizationMatrix(float[] matrix)
+ {
+ for (int y = 0; y < 8; y++)
+ {
+ for (int x = 0; x < 8; x++)
+ matrix[8*y + x] *= dctScalingFactors[y] * dctScalingFactors[x];
+ }
+ }
+
+ public static void scaleDequantizationMatrix(float[] matrix)
+ {
+ for (int y = 0; y < 8; y++)
+ {
+ for (int x = 0; x < 8; x++)
+ matrix[8*y + x] *= idctScalingFactors[y] * idctScalingFactors[x];
+ }
+ }
+
+ /**
+ * Fast forward DCT using AA&N.
+ * Taken from the book "JPEG still image data compression standard",
+ * by Pennebaker and Mitchell, chapter 4, figure "4-8".
+ */
+ public static void forwardDCT8(float[] vector)
+ {
+ float a00 = vector[0] + vector[7];
+ float a10 = vector[1] + vector[6];
+ float a20 = vector[2] + vector[5];
+ float a30 = vector[3] + vector[4];
+ float a40 = vector[3] - vector[4];
+ float a50 = vector[2] - vector[5];
+ float a60 = vector[1] - vector[6];
+ float a70 = vector[0] - vector[7];
+
+ float a01 = a00 + a30;
+ float a11 = a10 + a20;
+ float a21 = a10 - a20;
+ float a31 = a00 - a30;
+ // Avoid some negations:
+ // float a41 = -a40 - a50;
+ float neg_a41 = a40 + a50;
+ float a51 = a50 + a60;
+ float a61 = a60 + a70;
+
+ float a22 = a21 + a31;
+
+ float a23 = a22*A1;
+ float mul5 = (a61 - neg_a41)*A5;
+ float a43 = neg_a41*A2 - mul5;
+ float a53 = a51*A3;
+ float a63 = a61*A4 - mul5;
+
+ float a54 = a70 + a53;
+ float a74 = a70 - a53;
+
+ vector[0] = a01 + a11;
+ vector[4] = a01 - a11;
+ vector[2] = a31 + a23;
+ vector[6] = a31 - a23;
+ vector[5] = a74 + a43;
+ vector[1] = a54 + a63;
+ vector[7] = a54 - a63;
+ vector[3] = a74 - a43;
+ }
+
+ public static void forwardDCT8x8(float[] matrix)
+ {
+ float a00, a10, a20, a30, a40, a50, a60, a70;
+ float a01, a11, a21, a31, neg_a41, a51, a61;
+ float a22, a23, mul5, a43, a53, a63;
+ float a54, a74;
+
+ for (int i = 0; i < 8; i++)
+ {
+ a00 = matrix[8*i] + matrix[8*i + 7];
+ a10 = matrix[8*i + 1] + matrix[8*i + 6];
+ a20 = matrix[8*i + 2] + matrix[8*i + 5];
+ a30 = matrix[8*i + 3] + matrix[8*i + 4];
+ a40 = matrix[8*i + 3] - matrix[8*i + 4];
+ a50 = matrix[8*i + 2] - matrix[8*i + 5];
+ a60 = matrix[8*i + 1] - matrix[8*i + 6];
+ a70 = matrix[8*i] - matrix[8*i + 7];
+ a01 = a00 + a30;
+ a11 = a10 + a20;
+ a21 = a10 - a20;
+ a31 = a00 - a30;
+ neg_a41 = a40 + a50;
+ a51 = a50 + a60;
+ a61 = a60 + a70;
+ a22 = a21 + a31;
+ a23 = a22*A1;
+ mul5 = (a61 - neg_a41)*A5;
+ a43 = neg_a41*A2 - mul5;
+ a53 = a51*A3;
+ a63 = a61*A4 - mul5;
+ a54 = a70 + a53;
+ a74 = a70 - a53;
+ matrix[8*i] = a01 + a11;
+ matrix[8*i + 4] = a01 - a11;
+ matrix[8*i + 2] = a31 + a23;
+ matrix[8*i + 6] = a31 - a23;
+ matrix[8*i + 5] = a74 + a43;
+ matrix[8*i + 1] = a54 + a63;
+ matrix[8*i + 7] = a54 - a63;
+ matrix[8*i + 3] = a74 - a43;
+ }
+
+ for (int i = 0; i < 8; i++)
+ {
+ a00 = matrix[i] + matrix[56 + i];
+ a10 = matrix[8 + i] + matrix[48 + i];
+ a20 = matrix[16 + i] + matrix[40 + i];
+ a30 = matrix[24 + i] + matrix[32 + i];
+ a40 = matrix[24 + i] - matrix[32 + i];
+ a50 = matrix[16 + i] - matrix[40 + i];
+ a60 = matrix[8 + i] - matrix[48 + i];
+ a70 = matrix[i] - matrix[56 + i];
+ a01 = a00 + a30;
+ a11 = a10 + a20;
+ a21 = a10 - a20;
+ a31 = a00 - a30;
+ neg_a41 = a40 + a50;
+ a51 = a50 + a60;
+ a61 = a60 + a70;
+ a22 = a21 + a31;
+ a23 = a22*A1;
+ mul5 = (a61 - neg_a41)*A5;
+ a43 = neg_a41*A2 - mul5;
+ a53 = a51*A3;
+ a63 = a61*A4 - mul5;
+ a54 = a70 + a53;
+ a74 = a70 - a53;
+ matrix[i] = a01 + a11;
+ matrix[32 + i] = a01 - a11;
+ matrix[16 + i] = a31 + a23;
+ matrix[48 + i] = a31 - a23;
+ matrix[40 + i] = a74 + a43;
+ matrix[8 + i] = a54 + a63;
+ matrix[56 + i] = a54 - a63;
+ matrix[24 + i] = a74 - a43;
+ }
+ }
+
+ /**
+ * Fast inverse DCT using AA&N. This is taken from the beautiful
+ * http://vsr.informatik.tu-chemnitz.de/~jan/MPEG/HTML/IDCT.html
+ * which gives easy equations and properly explains constants and
+ * scaling factors. Terms have been inlined and the negation
+ * optimized out of existence.
+ */
+ public static void inverseDCT8(float[] vector)
+ {
+ // B1
+ float a2 = vector[2] - vector[6];
+ float a3 = vector[2] + vector[6];
+ float a4 = vector[5] - vector[3];
+ float tmp1 = vector[1] + vector[7];
+ float tmp2 = vector[3] + vector[5];
+ float a5 = tmp1 - tmp2;
+ float a6 = vector[1] - vector[7];
+ float a7 = tmp1 + tmp2;
+
+ // M
+ float tmp4 = C6*(a4 + a6);
+ // Eliminate the negative:
+ // float b4 = -Q*a4 - tmp4;
+ float neg_b4 = Q*a4 + tmp4;
+ float b6 = R*a6 - tmp4;
+ float b2 = a2*C4;
+ float b5 = a5*C4;
+
+ // A1
+ float tmp3 = b6 - a7;
+ float n0 = tmp3 - b5;
+ float n1 = vector[0] - vector[4];
+ float n2 = b2 - a3;
+ float n3 = vector[0] + vector[4];
+ float neg_n5 = neg_b4;
+
+ // A2
+ float m3 = n1 + n2;
+ float m4 = n3 + a3;
+ float m5 = n1 - n2;
+ float m6 = n3 - a3;
+ // float m7 = n5 - n0;
+ float neg_m7 = neg_n5 + n0;
+
+ // A3
+ vector[0] = m4 + a7;
+ vector[1] = m3 + tmp3;
+ vector[2] = m5 - n0;
+ vector[3] = m6 + neg_m7;
+ vector[4] = m6 - neg_m7;
+ vector[5] = m5 + n0;
+ vector[6] = m3 - tmp3;
+ vector[7] = m4 - a7;
+ }
+
+ public static void inverseDCT8x8(float[] matrix)
+ {
+ float a2, a3, a4, tmp1, tmp2, a5, a6, a7;
+ float tmp4, neg_b4, b6, b2, b5;
+ float tmp3, n0, n1, n2, n3, neg_n5;
+ float m3, m4, m5, m6, neg_m7;
+
+ for (int i = 0; i < 8; i++)
+ {
+ a2 = matrix[8*i + 2] - matrix[8*i + 6];
+ a3 = matrix[8*i + 2] + matrix[8*i + 6];
+ a4 = matrix[8*i + 5] - matrix[8*i + 3];
+ tmp1 = matrix[8*i + 1] + matrix[8*i + 7];
+ tmp2 = matrix[8*i + 3] + matrix[8*i + 5];
+ a5 = tmp1 - tmp2;
+ a6 = matrix[8*i + 1] - matrix[8*i + 7];
+ a7 = tmp1 + tmp2;
+ tmp4 = C6*(a4 + a6);
+ neg_b4 = Q*a4 + tmp4;
+ b6 = R*a6 - tmp4;
+ b2 = a2*C4;
+ b5 = a5*C4;
+ tmp3 = b6 - a7;
+ n0 = tmp3 - b5;
+ n1 = matrix[8*i] - matrix[8*i + 4];
+ n2 = b2 - a3;
+ n3 = matrix[8*i] + matrix[8*i + 4];
+ neg_n5 = neg_b4;
+ m3 = n1 + n2;
+ m4 = n3 + a3;
+ m5 = n1 - n2;
+ m6 = n3 - a3;
+ neg_m7 = neg_n5 + n0;
+ matrix[8*i] = m4 + a7;
+ matrix[8*i + 1] = m3 + tmp3;
+ matrix[8*i + 2] = m5 - n0;
+ matrix[8*i + 3] = m6 + neg_m7;
+ matrix[8*i + 4] = m6 - neg_m7;
+ matrix[8*i + 5] = m5 + n0;
+ matrix[8*i + 6] = m3 - tmp3;
+ matrix[8*i + 7] = m4 - a7;
+ }
+
+ for (int i = 0; i < 8; i++)
+ {
+ a2 = matrix[16 + i] - matrix[48 + i];
+ a3 = matrix[16 + i] + matrix[48 + i];
+ a4 = matrix[40 + i] - matrix[24 + i];
+ tmp1 = matrix[8 + i] + matrix[56 + i];
+ tmp2 = matrix[24 + i] + matrix[40 + i];
+ a5 = tmp1 - tmp2;
+ a6 = matrix[8 + i] - matrix[56 + i];
+ a7 = tmp1 + tmp2;
+ tmp4 = C6*(a4 + a6);
+ neg_b4 = Q*a4 + tmp4;
+ b6 = R*a6 - tmp4;
+ b2 = a2*C4;
+ b5 = a5*C4;
+ tmp3 = b6 - a7;
+ n0 = tmp3 - b5;
+ n1 = matrix[i] - matrix[32 + i];
+ n2 = b2 - a3;
+ n3 = matrix[i] + matrix[32 + i];
+ neg_n5 = neg_b4;
+ m3 = n1 + n2;
+ m4 = n3 + a3;
+ m5 = n1 - n2;
+ m6 = n3 - a3;
+ neg_m7 = neg_n5 + n0;
+ matrix[i] = m4 + a7;
+ matrix[8 + i] = m3 + tmp3;
+ matrix[16 + i] = m5 - n0;
+ matrix[24 + i] = m6 + neg_m7;
+ matrix[32 + i] = m6 - neg_m7;
+ matrix[40 + i] = m5 + n0;
+ matrix[48 + i] = m3 - tmp3;
+ matrix[56 + i] = m4 - a7;
+ }
+ }
+
+ private static float[][] REFERENCE_inverseDCT(float[][] matrix)
+ {
+ float[][] ret = new float[8][8];
+ for (int y = 0; y < 8; y++)
+ {
+ for (int x = 0; x < 8; x++)
+ {
+ float sum = 0;
+ for (int u = 0; u < 8; u++)
+ {
+ for (int v = 0; v < 8; v++)
+ {
+ float cu = (u == 0) ? ((float)(1.0/Math.sqrt(2))) : 1;
+ float cv = (v == 0) ? ((float)(1.0/Math.sqrt(2))) : 1;
+ sum += cu*cv*matrix[v][u]*
+ Math.cos((2*x + 1)*u*Math.PI/16)*
+ Math.cos((2*y + 1)*v*Math.PI/16);
+ }
+ }
+ ret[y][x] = 0.25f * sum;
+ }
+ }
+ return ret;
+ }
+
+ private static float[] REFERENCE_inverseDCT(float[] vector)
+ {
+ float[] ret = new float[8];
+ for (int x = 0; x < 8; x++)
+ {
+ float sum = 0;
+ for (int u = 0; u < 8; u++)
+ {
+ float cu = (u == 0) ? ((float)(1.0/Math.sqrt(2))) : 1;
+ sum += cu*vector[u]*
+ Math.cos((2*x + 1)*u*Math.PI/16);
+ }
+ ret[x] = 0.5f * sum;
+ }
+ return ret;
+ }
+
+ private static float[][] REFERENCE_forwardDCT(float[][] matrix)
+ {
+ float[][] ret = new float[8][8];
+ for (int u = 0; u < 8; u++)
+ {
+ for (int v = 0; v < 8; v++)
+ {
+ float sum = 0;
+ float cu = (u == 0) ? ((float)(1.0/Math.sqrt(2))) : 1;
+ float cv = (v == 0) ? ((float)(1.0/Math.sqrt(2))) : 1;
+ for (int x = 0; x < 8; x++)
+ {
+ for (int y = 0; y < 8; y++)
+ {
+ sum += matrix[y][x] *
+ Math.cos((2*x + 1)*u*Math.PI/16) *
+ Math.cos((2*y + 1)*v*Math.PI/16);
+ }
+ }
+ ret[v][u] = 0.25f * cu * cv * sum;
+ }
+ }
+ return ret;
+ }
+
+ private static float[] REFERENCE_forwardDCT(float[] vector)
+ {
+ float[] ret = new float[8];
+ for (int u = 0; u < 8; u++)
+ {
+ float sum = 0;
+ float cu = (u == 0) ? ((float)(1.0/Math.sqrt(2))) : 1;
+ for (int x = 0; x < 8; x++)
+ {
+ sum += vector[x]*
+ Math.cos((2*x + 1)*u*Math.PI/16);
+ }
+ ret[u] = 0.5f * cu * sum;
+ }
+ return ret;
+ }
+
+ private static boolean isClose(float a, float b)
+ {
+ return Math.abs(a - b) < 0.001;
+ }
+
+ private static void testVectors() throws Exception
+ {
+ float[] originalData = new float[8];
+ for (int i = 0; i < 8; i++)
+ originalData[i] = i;
+
+ float[] transformed = REFERENCE_forwardDCT(originalData);
+ float[] reversed = REFERENCE_inverseDCT(transformed);
+ for (int i = 0; i < 8; i++)
+ {
+ if (!isClose(originalData[i], reversed[i]))
+ {
+ throw new Exception("Reference transforms broken, " +
+ "at x=" + i + ", " +
+ "original " + originalData[i] + " " +
+ "actual " + reversed[i]);
+ }
+ }
+
+ float[] data = (float[]) originalData.clone();
+ forwardDCT8(data);
+ scaleQuantizationVector(data);
+ for (int i = 0; i < 8; i++)
+ {
+ if (!isClose(data[i], transformed[i]))
+ {
+ throw new Exception("Forward transform broken, " +
+ "at x=" + i + " " +
+ "reference " + originalData[i] + ", " +
+ "actual " + data[i]);
+ }
+ }
+ scaleDequantizationVector(data);
+ inverseDCT8(data);
+ for (int i = 0; i < 8; i++)
+ {
+ if (!isClose(data[i], originalData[i]))
+ {
+ throw new Exception("Inverse transform broken, " +
+ "at x=" + i + " " +
+ "reference " + originalData[i] + ", " +
+ "actual " + data[i]);
+ }
+ }
+ }
+
+ private static void testMatrices() throws Exception
+ {
+ float[] originalData = new float[8*8];
+ float[][] originalData8x8 = new float[8][8];
+ for (int y = 0; y < 8; y++)
+ {
+ for (int x = 0; x < 8; x++)
+ {
+ float value = 8*y + x;
+ originalData8x8[y][x] = value;
+ originalData[8*y + x] = value;
+ }
+ }
+
+ float[][] transformed8x8 = REFERENCE_forwardDCT(originalData8x8);
+ float[][] reversed8x8 = REFERENCE_inverseDCT(transformed8x8);
+ for (int y = 0; y < 8; y++)
+ {
+ for (int x = 0; x < 8; x++)
+ {
+ if (!isClose(originalData8x8[y][x], reversed8x8[y][x]))
+ {
+ throw new Exception("Reference transforms broken, " +
+ "at x=" + x + ",y=" + y + " " +
+ "original " + originalData8x8[y][x] + ", " +
+ "actual " + reversed8x8[y][x]);
+ }
+ }
+ }
+
+ float[] data = (float[]) originalData.clone();
+ forwardDCT8x8(data);
+ scaleQuantizationMatrix(data);
+ for (int y = 0; y < 8; y++)
+ {
+ for (int x = 0; x < 8; x++)
+ {
+ if (!isClose(transformed8x8[y][x], data[8*y + x]))
+ {
+ throw new Exception("Forward transform broken, " +
+ "at x=" + x + ",y=" + y + " " +
+ "reference " + originalData8x8[y][x] + ", " +
+ "actual " + data[8*y + x]);
+ }
+ }
+ }
+ scaleDequantizationMatrix(data);
+ inverseDCT8x8(data);
+ for (int y = 0; y < 8; y++)
+ {
+ for (int x = 0; x < 8; x++)
+ {
+ if (!isClose(originalData8x8[y][x], data[8*y + x]))
+ {
+ throw new Exception("Inverse transform broken, " +
+ "at x=" + x + ",y=" + y + " " +
+ "reference " + originalData8x8[y][x] + ", " +
+ "actual " + data[8*y + x]);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
Propchange: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/decoder/DCT.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/decoder/JpegDecoder.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/decoder/JpegDecoder.java?rev=1090754&view=auto
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/decoder/JpegDecoder.java (added)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/decoder/JpegDecoder.java Sun Apr 10 09:08:42 2011
@@ -0,0 +1,485 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * under the License.
+ */
+
+package org.apache.sanselan.formats.jpeg.decoder;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DirectColorModel;
+import java.awt.image.WritableRaster;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Properties;
+import org.apache.sanselan.ImageReadException;
+import org.apache.sanselan.common.BinaryFileParser;
+import org.apache.sanselan.common.byteSources.ByteSource;
+import org.apache.sanselan.formats.jpeg.Block;
+import org.apache.sanselan.formats.jpeg.JpegConstants;
+import org.apache.sanselan.formats.jpeg.JpegUtils;
+import org.apache.sanselan.formats.jpeg.ZigZag;
+import org.apache.sanselan.formats.jpeg.segments.DHTSegment;
+import org.apache.sanselan.formats.jpeg.segments.DQTSegment;
+import org.apache.sanselan.formats.jpeg.segments.SOFNSegment;
+import org.apache.sanselan.formats.jpeg.segments.SOSSegment;
+
+public class JpegDecoder extends BinaryFileParser implements JpegUtils.Visitor,
+ JpegConstants
+{
+ /*
+ * JPEG is an advanced image format that takes
+ * significant computation to decode. Keep
+ * decoding fast:
+ * - Don't allocate memory inside loops,
+ * allocate it once and reuse.
+ * - Minimize calculations
+ * per pixel and per block (using lookup tables
+ * for YCbCr->RGB conversion doubled performance).
+ * - Math.round() is slow, use (int)(x+0.5f) instead
+ * for positive numbers.
+ */
+
+ private DQTSegment.QuantizationTable[] quantizationTables = new DQTSegment.QuantizationTable[4];
+ private DHTSegment.HuffmanTable[] huffmanDCTables = new DHTSegment.HuffmanTable[4];
+ private DHTSegment.HuffmanTable[] huffmanACTables = new DHTSegment.HuffmanTable[4];
+ private SOFNSegment sofnSegment;
+ private SOSSegment sosSegment;
+ private float[][] scaledQuantizationTables = new float[4][];
+ private BufferedImage image = null;
+ private ImageReadException imageReadException = null;
+ private IOException ioException = null;
+
+ public boolean beginSOS()
+ {
+ return true;
+ }
+
+ public void visitSOS(int marker, byte markerBytes[],
+ byte imageData[])
+ {
+ ByteArrayInputStream is = new ByteArrayInputStream(imageData);
+ try
+ {
+ int segmentLength = read2Bytes("segmentLength", is,
+ "Not a Valid JPEG File");
+ byte[] sosSegmentBytes = readByteArray("SOSSegment",
+ segmentLength - 2, is, "Not a Valid JPEG File");
+ sosSegment = new SOSSegment(marker, sosSegmentBytes);
+
+ int hMax = 0;
+ int vMax = 0;
+ for (int i = 0; i < sofnSegment.numberOfComponents; i++)
+ {
+ hMax = Math.max(hMax, sofnSegment.components[i].horizontalSamplingFactor);
+ vMax = Math.max(vMax, sofnSegment.components[i].verticalSamplingFactor);
+ }
+ int hSize = 8*hMax;
+ int vSize = 8*vMax;
+
+ JpegInputStream bitInputStream = new JpegInputStream(is);
+ int xMCUs = (sofnSegment.width + hSize - 1) / hSize;
+ int yMCUs = (sofnSegment.height + vSize - 1) / vSize;
+ Block[] mcu = allocateMCUMemory();
+ Block[] scaledMCU = new Block[mcu.length];
+ for (int i = 0; i < scaledMCU.length; i++)
+ scaledMCU[i] = new Block(hSize, vSize);
+ int[] preds = new int[sofnSegment.numberOfComponents];
+ ColorModel colorModel;
+ WritableRaster raster;
+ if (sofnSegment.numberOfComponents == 3)
+ {
+ colorModel = new DirectColorModel(24,
+ 0x00ff0000, 0x0000ff00, 0x000000ff);
+ raster = WritableRaster.createPackedRaster(DataBuffer.TYPE_INT,
+ sofnSegment.width, sofnSegment.height,
+ new int[]{0x00ff0000,0x0000ff00,0x000000ff}, null);
+ }
+ else if (sofnSegment.numberOfComponents == 1)
+ {
+ colorModel = new DirectColorModel(24,
+ 0x00ff0000, 0x0000ff00, 0x000000ff);
+ raster = WritableRaster.createPackedRaster(DataBuffer.TYPE_INT,
+ sofnSegment.width, sofnSegment.height,
+ new int[]{0x00ff0000,0x0000ff00,0x000000ff}, null);
+ // FIXME: why do images come out too bright with CS_GRAY?
+// colorModel = new ComponentColorModel(
+// ColorSpace.getInstance(ColorSpace.CS_GRAY), false, true,
+// Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
+// raster = colorModel.createCompatibleWritableRaster(
+// sofnSegment.width, sofnSegment.height);
+ }
+ else
+ throw new ImageReadException(sofnSegment.numberOfComponents +
+ " components are invalid or unsupported");
+ DataBuffer dataBuffer = raster.getDataBuffer();
+
+
+ for (int y1 = 0; y1 < vSize*yMCUs; y1 += vSize)
+ {
+ for (int x1 = 0; x1 < hSize*xMCUs; x1 += hSize)
+ {
+ readMCU(bitInputStream, preds, mcu);
+ rescaleMCU(mcu, hSize, vSize, scaledMCU);
+ int srcRowOffset = 0;
+ int dstRowOffset = y1*sofnSegment.width + x1;
+ for (int y2 = 0; y2 < vSize && y1 + y2 < sofnSegment.height; y2++)
+ {
+ for (int x2 = 0; x2 < hSize && x1 + x2 < sofnSegment.width; x2++)
+ {
+ if (scaledMCU.length == 3)
+ {
+ int Y = scaledMCU[0].samples[srcRowOffset + x2];
+ int Cb = scaledMCU[1].samples[srcRowOffset + x2];
+ int Cr = scaledMCU[2].samples[srcRowOffset + x2];
+ int rgb = YCbCrConverter.convertYCbCrToRGB(Y, Cb, Cr);
+ dataBuffer.setElem(dstRowOffset + x2, rgb);
+ }
+ else if (mcu.length == 1)
+ {
+ int Y = scaledMCU[0].samples[srcRowOffset + x2];
+ dataBuffer.setElem(dstRowOffset + x2,
+ (Y << 16) | (Y << 8) | Y);
+ }
+ else
+ throw new ImageReadException("Unsupported JPEG with " +
+ mcu.length + " components");
+ }
+ srcRowOffset += hSize;
+ dstRowOffset += sofnSegment.width;
+ }
+ }
+ }
+ image = new BufferedImage(colorModel, raster,
+ colorModel.isAlphaPremultiplied(), new Properties());
+ //byte[] remainder = super.getStreamBytes(is);
+ //for (int i = 0; i < remainder.length; i++)
+ //{
+ // System.out.println("" + i + " = " + Integer.toHexString(remainder[i]));
+ //}
+ }
+ catch (ImageReadException imageReadEx)
+ {
+ imageReadException = imageReadEx;
+ }
+ catch (IOException ioEx)
+ {
+ ioException = ioEx;
+ }
+ catch (RuntimeException ex)
+ {
+ // Corrupt images can throw NPE and IOOBE
+ imageReadException = new ImageReadException("Error parsing JPEG", ex);
+ }
+ }
+
+ public boolean visitSegment(int marker, byte[] markerBytes,
+ int segmentLength, byte[] segmentLengthBytes,
+ byte[] segmentData) throws ImageReadException, IOException
+ {
+ final int[] sofnSegments = {
+ SOF0Marker,
+ SOF1Marker, SOF2Marker, SOF3Marker, SOF5Marker, SOF6Marker,
+ SOF7Marker, SOF9Marker, SOF10Marker, SOF11Marker, SOF13Marker,
+ SOF14Marker, SOF15Marker,
+ };
+
+ if (Arrays.binarySearch(sofnSegments, marker) >= 0)
+ {
+ if (marker != SOF0Marker)
+ throw new ImageReadException("Only sequential, baseline JPEGs " +
+ "are supported at the moment");
+ sofnSegment = new SOFNSegment(marker, segmentData);
+ }
+ else if (marker == DQTMarker)
+ {
+ DQTSegment dqtSegment = new DQTSegment(marker, segmentData);
+ for (int i = 0; i < dqtSegment.quantizationTables.size(); i++)
+ {
+ DQTSegment.QuantizationTable table = (DQTSegment.QuantizationTable)
+ dqtSegment.quantizationTables.get(i);
+ if (0 > table.destinationIdentifier ||
+ table.destinationIdentifier >= quantizationTables.length)
+ throw new ImageReadException("Invalid quantization table identifier " +
+ table.destinationIdentifier);
+ quantizationTables[table.destinationIdentifier] = table;
+ int[] quantizationMatrixInt = new int[64];
+ ZigZag.zigZagToBlock(table.elements, quantizationMatrixInt);
+ float[] quantizationMatrixFloat = new float[64];
+ for (int j = 0; j < 64; j++)
+ quantizationMatrixFloat[j] = quantizationMatrixInt[j];
+ DCT.scaleDequantizationMatrix(quantizationMatrixFloat);
+ scaledQuantizationTables[table.destinationIdentifier] =
+ quantizationMatrixFloat;
+ }
+ }
+ else if (marker == DHTMarker)
+ {
+ DHTSegment dhtSegment = new DHTSegment(marker, segmentData);
+ for (int i = 0; i < dhtSegment.huffmanTables.size(); i++)
+ {
+ DHTSegment.HuffmanTable table = (DHTSegment.HuffmanTable)
+ dhtSegment.huffmanTables.get(i);
+ DHTSegment.HuffmanTable[] tables;
+ if (table.tableClass == 0)
+ tables = huffmanDCTables;
+ else if (table.tableClass == 1)
+ tables = huffmanACTables;
+ else
+ throw new ImageReadException("Invalid huffman table class " +
+ table.tableClass);
+ if (0 > table.destinationIdentifier ||
+ table.destinationIdentifier >= tables.length)
+ throw new ImageReadException("Invalid huffman table identifier " +
+ table.destinationIdentifier);
+ tables[table.destinationIdentifier] = table;
+ }
+ }
+ return true;
+ }
+
+ private void rescaleMCU(Block[] dataUnits, int hSize, int vSize, Block[] ret)
+ {
+ for (int i = 0; i < dataUnits.length; i++)
+ {
+ Block block = dataUnits[i];
+ if (block.width == hSize && block.height == vSize)
+ System.arraycopy(block.samples, 0, ret[i].samples, 0, hSize*vSize);
+ else
+ {
+ int hScale = hSize / block.width;
+ int vScale = vSize / block.height;
+ if (hScale == 2 && vScale == 2)
+ {
+ int srcRowOffset = 0;
+ int dstRowOffset = 0;
+ for (int y = 0; y < block.height; y++)
+ {
+ for (int x = 0; x < hSize; x++)
+ {
+ int sample = block.samples[srcRowOffset + (x >> 1)];
+ ret[i].samples[dstRowOffset + x] = sample;
+ ret[i].samples[dstRowOffset + hSize + x] = sample;
+ }
+ srcRowOffset += block.width;
+ dstRowOffset += 2*hSize;
+ }
+ }
+ else
+ {
+ // FIXME: optimize
+ int dstRowOffset = 0;
+ for (int y = 0; y < vSize; y++)
+ {
+ for (int x = 0; x < hSize; x++)
+ {
+ ret[i].samples[dstRowOffset + x] =
+ block.samples[(y/vScale)*block.width + (x/hScale)];
+ }
+ dstRowOffset += hSize;
+ }
+ }
+ }
+ }
+ }
+
+ private Block[] allocateMCUMemory() throws ImageReadException
+ {
+ Block[] mcu = new Block[sosSegment.numberOfComponents];
+ for (int i = 0; i < sosSegment.numberOfComponents; i++)
+ {
+ SOSSegment.Component scanComponent = sosSegment.components[i];
+ SOFNSegment.Component frameComponent = null;
+ for (int j = 0; j < sofnSegment.numberOfComponents; j++)
+ {
+ if (sofnSegment.components[j].componentIdentifier ==
+ scanComponent.scanComponentSelector)
+ {
+ frameComponent = sofnSegment.components[j];
+ break;
+ }
+ }
+ if (frameComponent == null)
+ throw new ImageReadException("Invalid component");
+ Block fullBlock = new Block(
+ 8*frameComponent.horizontalSamplingFactor,
+ 8*frameComponent.verticalSamplingFactor);
+ mcu[i] = fullBlock;
+ }
+ return mcu;
+ }
+
+ private int[] zz = new int[64];
+ private int[] blockInt = new int[64];
+ private float[] block = new float[64];
+ private void readMCU(JpegInputStream is, int[] preds, Block[] mcu)
+ throws IOException, ImageReadException
+ {
+ for (int i = 0; i < sosSegment.numberOfComponents; i++)
+ {
+ SOSSegment.Component scanComponent = sosSegment.components[i];
+ SOFNSegment.Component frameComponent = null;
+ for (int j = 0; j < sofnSegment.numberOfComponents; j++)
+ {
+ if (sofnSegment.components[j].componentIdentifier ==
+ scanComponent.scanComponentSelector)
+ {
+ frameComponent = sofnSegment.components[j];
+ break;
+ }
+ }
+ if (frameComponent == null)
+ throw new ImageReadException("Invalid component");
+ Block fullBlock = mcu[i];
+ for (int y = 0; y < frameComponent.verticalSamplingFactor; y++)
+ {
+ for (int x = 0; x < frameComponent.horizontalSamplingFactor; x++)
+ {
+ Arrays.fill(zz, 0);
+ // page 104 of T.81
+ int t = decode(is,
+ huffmanDCTables[scanComponent.dcCodingTableSelector]);
+ int diff = receive(t, is);
+ diff = extend(diff, t);
+ zz[0] = preds[i] + diff;
+ preds[i] = zz[0];
+
+ // "Decode_AC_coefficients", figure F.13, page 106 of T.81
+ int k = 1;
+ while (true)
+ {
+ int rs = decode(is,
+ huffmanACTables[scanComponent.acCodingTableSelector]);
+ int ssss = rs & 0xf;
+ int rrrr = rs >> 4;
+ int r = rrrr;
+
+ if (ssss == 0)
+ {
+ if (r == 15)
+ k += 16;
+ else
+ break;
+ }
+ else
+ {
+ k += r;
+
+ // "Decode_ZZ(k)", figure F.14, page 107 of T.81
+ zz[k] = receive(ssss, is);
+ zz[k] = extend(zz[k], ssss);
+
+ if (k == 63)
+ break;
+ else
+ k++;
+ }
+ }
+
+ final int shift = (1 << (sofnSegment.precision - 1));
+ final int max = (1 << sofnSegment.precision) - 1;
+
+ float[] scaledQuantizationTable =
+ scaledQuantizationTables[frameComponent.quantTabDestSelector];
+ ZigZag.zigZagToBlock(zz, blockInt);
+ for (int j = 0; j < 64; j++)
+ block[j] = blockInt[j] * scaledQuantizationTable[j];
+ DCT.inverseDCT8x8(block);
+
+ int dstRowOffset = 8*y*8*frameComponent.horizontalSamplingFactor +
+ 8*x;
+ int srcNext = 0;
+ for (int yy = 0; yy < 8; yy++)
+ {
+ for (int xx = 0; xx < 8; xx++)
+ {
+ float sample = block[srcNext++];
+ sample += shift;
+ int result;
+ if (sample < 0)
+ result = 0;
+ else if (sample > max)
+ result = max;
+ else
+ result = fastRound(sample);
+ fullBlock.samples[dstRowOffset + xx] = result;
+ }
+ dstRowOffset += 8*frameComponent.horizontalSamplingFactor;
+ }
+ }
+ }
+ }
+ }
+
+ private static int fastRound(float x)
+ {
+ return (int) (x + 0.5f);
+ }
+
+ private int extend(int v, int t)
+ {
+ // "EXTEND", section F.2.2.1, figure F.12, page 105 of T.81
+ int vt = (1 << (t - 1));
+ while (v < vt)
+ {
+ vt = (-1 << t) + 1;
+ v += vt;
+ }
+ return v;
+ }
+
+ private int receive(int ssss, JpegInputStream is)
+ throws IOException, ImageReadException
+ {
+ // "RECEIVE", section F.2.2.4, figure F.17, page 110 of T.81
+ int i = 0;
+ int v = 0;
+ while (i != ssss)
+ {
+ i++;
+ v = (v << 1) + is.nextBit();
+ }
+ return v;
+ }
+
+ private int decode(JpegInputStream is, DHTSegment.HuffmanTable huffmanTable)
+ throws IOException, ImageReadException
+ {
+ // "DECODE", section F.2.2.3, figure F.16, page 109 of T.81
+ int i = 1;
+ int code = is.nextBit();
+ while (code > huffmanTable.maxCode[i])
+ {
+ i++;
+ code = (code << 1) | is.nextBit();
+ }
+ int j = huffmanTable.valPtr[i];
+ j += code - huffmanTable.minCode[i];
+ int value = huffmanTable.huffVal[j];
+ return value;
+ }
+
+ public BufferedImage decode(ByteSource byteSource)
+ throws IOException, ImageReadException
+ {
+ JpegUtils jpegUtils = new JpegUtils();
+ jpegUtils.traverseJFIF(byteSource, this);
+ if (imageReadException != null)
+ throw imageReadException;
+ if (ioException != null)
+ throw ioException;
+ return image;
+ }
+}
Propchange: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/decoder/JpegDecoder.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/decoder/JpegInputStream.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/decoder/JpegInputStream.java?rev=1090754&view=auto
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/decoder/JpegInputStream.java (added)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/decoder/JpegInputStream.java Sun Apr 10 09:08:42 2011
@@ -0,0 +1,64 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * under the License.
+ */
+
+package org.apache.sanselan.formats.jpeg.decoder;
+
+import java.io.IOException;
+import java.io.InputStream;
+import org.apache.sanselan.ImageReadException;
+import org.apache.sanselan.formats.jpeg.JpegConstants;
+
+public class JpegInputStream
+{
+ // Figure F.18, F.2.2.5, page 111 of ITU-T T.81
+ private final InputStream is;
+ private int cnt = 0;
+ private int b;
+
+ public JpegInputStream(InputStream is)
+ {
+ this.is = is;
+ }
+
+ public int nextBit()
+ throws IOException, ImageReadException
+ {
+ if (cnt == 0)
+ {
+ b = is.read();
+ if (b < 0)
+ throw new ImageReadException("Premature End of File");
+ cnt = 8;
+ if (b == 0xff)
+ {
+ int b2 = is.read();
+ if (b2 < 0)
+ throw new ImageReadException("Premature End of File");
+ if (b2 != 0)
+ {
+ if (b2 == (0xff & JpegConstants.DNLMarker))
+ throw new ImageReadException("DNL not yet supported");
+ else
+ throw new ImageReadException("Invalid marker found " +
+ "in entropy data");
+ }
+ }
+ }
+ int bit = (b >> 7) & 0x1;
+ cnt--;
+ b <<= 1;
+ return bit;
+ }
+}
Propchange: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/decoder/JpegInputStream.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/decoder/YCbCrConverter.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/decoder/YCbCrConverter.java?rev=1090754&view=auto
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/decoder/YCbCrConverter.java (added)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/decoder/YCbCrConverter.java Sun Apr 10 09:08:42 2011
@@ -0,0 +1,118 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * under the License.
+ */
+
+package org.apache.sanselan.formats.jpeg.decoder;
+
+public class YCbCrConverter
+{
+ private static final int[] reds = new int[256*256];
+ private static final int[] blues = new int[256*256];
+ private static final int[] greens1 = new int[256*256];
+ private static final int[] greens2 = new int[256*512];
+
+ static
+ {
+ /*
+ * Why use (Cr << 8) | Y
+ * and not (Y << 8) | Cr as the index?
+ * Y changes often, while Cb and Cr is usually subsampled
+ * less often and repeats itself between adjacent pixels,
+ * so using it as the high order byte gives
+ * higher locality of reference.
+ */
+ for (int Y = 0; Y < 256; Y++)
+ {
+ for (int Cr = 0; Cr < 256; Cr++)
+ {
+ int r = Y + fastRound(1.402f*(Cr-128));
+ if (r < 0) r = 0;
+ if (r > 255) r = 255;
+ reds[(Cr << 8) | Y] = r << 16;
+ }
+ }
+ for (int Y = 0; Y < 256; Y++)
+ {
+ for (int Cb = 0; Cb < 256; Cb++)
+ {
+ int b = Y + fastRound(1.772f*(Cb-128));
+ if (b < 0) b = 0;
+ if (b > 255) b = 255;
+ blues[(Cb << 8) | Y] = b;
+ }
+ }
+ // green is the hardest
+ // Math.round((float)(Y - 0.34414*(Cb-128) - 0.71414*(Cr-128)))
+ // but Y is integral
+ // = Y - Math.round((float)(0.34414*(Cb-128) + 0.71414*(Cr-128)))
+ // = Y - Math.round(f(Cb, Cr))
+ // where
+ // f(Cb, Cr) = 0.34414*(Cb-128) + 0.71414*(Cr-128)
+ // Cb and Cr terms each vary from 255-128 = 127 to 0-128 = -128
+ // Linear function, so only examine endpoints:
+ // Cb term Cr term Result
+ // 127 127 134.4
+ // -128 -128 -135.4
+ // 127 -128 -47.7
+ // -128 127 46.6
+ // Thus with -135 being the minimum and 134 the maximum,
+ // there is a range of 269 values,
+ // and 135 needs to be added to make it zero-based.
+
+ // As for Y - f(Cb, Cr)
+ // the range becomes:
+ // Y f(Cb, Cr)
+ // 255 -135
+ // 255 134
+ // 0 -135
+ // 0 134
+ // thus the range is [-134,390] and has 524 values
+ // but is clamped to [0, 255]
+ for (int Cb = 0; Cb < 256; Cb++)
+ {
+ for (int Cr = 0; Cr < 256; Cr++)
+ {
+ int value = fastRound(0.34414f*(Cb-128) + 0.71414f*(Cr-128));
+ greens1[(Cb << 8) | Cr] = value + 135;
+ }
+ }
+ for (int Y = 0; Y < 256; Y++)
+ {
+ for (int value = 0; value < 270; value++)
+ {
+ int green = Y - (value - 135);
+ if (green < 0)
+ green = 0;
+ else if (green > 255)
+ green = 255;
+ greens2[(value << 8) | Y] = green << 8;
+ }
+ }
+ }
+
+ private static int fastRound(float x)
+ {
+ // Math.round() is very slow
+ return (int) (x + 0.5f);
+ }
+
+ public static int convertYCbCrToRGB(int Y, int Cb, int Cr)
+ {
+ int r = reds[(Cr << 8) | Y];
+ int g1 = greens1[(Cb << 8) | Cr];
+ int g = greens2[(g1 << 8) | Y];
+ int b = blues[(Cb << 8) | Y];
+ return r | g | b;
+ }
+}
Propchange: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/decoder/YCbCrConverter.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/DHTSegment.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/DHTSegment.java?rev=1090754&view=auto
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/DHTSegment.java (added)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/DHTSegment.java Sun Apr 10 09:08:42 2011
@@ -0,0 +1,167 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * under the License.
+ */
+
+package org.apache.sanselan.formats.jpeg.segments;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import org.apache.sanselan.ImageReadException;
+
+public class DHTSegment extends Segment
+{
+ public final ArrayList huffmanTables = new ArrayList();
+
+ public static class HuffmanTable
+ {
+ // some arrays are better off one-based
+ // to avoid subtractions by one later when indexing them
+ public final int tableClass;
+ public final int destinationIdentifier;
+ public final int[] bits; // 1-based
+ public final int[] huffVal; // 0-based
+
+ // derived properties:
+ public final int[] huffSize = new int[16 * 256]; // 0-based
+ public final int[] huffCode; // 0-based
+ public final int[] minCode = new int[1 + 16]; // 1-based
+ public final int[] maxCode = new int[1 + 16]; // 1-based
+ public final int[] valPtr = new int[1 + 16]; // 1-based
+
+ public HuffmanTable(int tableClass, int destinationIdentifier,
+ int[] bits, int[] huffVal)
+ {
+ this.tableClass = tableClass;
+ this.destinationIdentifier = destinationIdentifier;
+ this.bits = bits;
+ this.huffVal = huffVal;
+
+ // "generate_size_table", section C.2, figure C.1, page 51 of ITU-T T.81:
+ int k = 0;
+ int i = 1;
+ int j = 1;
+ int lastK = -1;
+ while (true)
+ {
+ if (j > bits[i])
+ {
+ i++;
+ j = 1;
+ if (i > 16)
+ {
+ huffSize[k] = 0;
+ lastK = k;
+ break;
+ }
+ }
+ else
+ {
+ huffSize[k] = i;
+ k++;
+ j++;
+ }
+ }
+
+ // "generate_code_table", section C.2, figure C.2, page 52 of ITU-T T.81:
+ k = 0;
+ int code = 0;
+ int si = huffSize[0];
+ huffCode = new int[lastK];
+ while (true)
+ {
+ huffCode[k] = code;
+ code++;
+ k++;
+
+ if (huffSize[k] == si)
+ continue;
+ if (huffSize[k] == 0)
+ break;
+ do
+ {
+ code <<= 1;
+ si++;
+ } while (huffSize[k] != si);
+ }
+
+ // "Decoder_tables", section F.2.2.3, figure F.15, page 108 of T.81:
+ i = 0;
+ j = 0;
+ while (true)
+ {
+ i++;
+ if (i > 16)
+ break;
+ if (bits[i] == 0)
+ maxCode[i] = -1;
+ else
+ {
+ valPtr[i] = j;
+ minCode[i] = huffCode[j];
+ j += bits[i] - 1;
+ maxCode[i] = huffCode[j];
+ j++;
+ }
+ }
+
+ }
+ }
+
+
+ public DHTSegment(int marker, byte[] segmentData)
+ throws ImageReadException, IOException
+ {
+ this(marker, segmentData.length, new ByteArrayInputStream(segmentData));
+ }
+
+ public DHTSegment(int marker, int length, InputStream is)
+ throws ImageReadException, IOException
+ {
+ super(marker, length);
+
+ while (length > 0)
+ {
+ int tableClassAndDestinationId =
+ 0xff & readByte("TableClassAndDestinationId",
+ is, "Not a Valid JPEG File");
+ length--;
+ int tableClass = (tableClassAndDestinationId >> 4) & 0xf;
+ int destinationIdentifier = tableClassAndDestinationId & 0xf;
+ int[] bits = new int[1 + 16];
+ int bitsSum = 0;
+ for (int i = 1; i < bits.length; i++)
+ {
+ bits[i] = 0xff & readByte("Li", is, "Not a Valid JPEG File");
+ length--;
+ bitsSum += bits[i];
+ }
+ int[] huffVal = new int[bitsSum];
+ for (int i = 0; i < bitsSum; i++)
+ {
+ huffVal[i] = 0xff & readByte("Vij", is, "Not a Valid JPEG File");
+ length--;
+ }
+
+ huffmanTables.add(new HuffmanTable(tableClass,
+ destinationIdentifier, bits, huffVal));
+ }
+ }
+
+ public String getDescription()
+ {
+ return "DHT (" + getSegmentType() + ")";
+ }
+}
Propchange: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/DHTSegment.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/DQTSegment.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/DQTSegment.java?rev=1090754&view=auto
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/DQTSegment.java (added)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/DQTSegment.java Sun Apr 10 09:08:42 2011
@@ -0,0 +1,94 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * under the License.
+ */
+
+package org.apache.sanselan.formats.jpeg.segments;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import org.apache.sanselan.ImageReadException;
+
+public class DQTSegment extends Segment
+{
+ public final ArrayList quantizationTables = new ArrayList();
+
+ public static class QuantizationTable
+ {
+ public final int precision;
+ public final int destinationIdentifier;
+ public final int[] elements;
+
+ public QuantizationTable(int precision, int destinationIdentifier,
+ int[] elements)
+ {
+ this.precision = precision;
+ this.destinationIdentifier = destinationIdentifier;
+ this.elements = elements;
+ }
+ }
+
+ public DQTSegment(int marker, byte[] segmentData)
+ throws ImageReadException, IOException
+ {
+ this(marker, segmentData.length, new ByteArrayInputStream(segmentData));
+ }
+
+ public DQTSegment(int marker, int length, InputStream is)
+ throws ImageReadException, IOException
+ {
+ super(marker, length);
+
+ while (length > 0)
+ {
+ int precisionAndDestination = readByte(
+ "QuantizationTablePrecisionAndDestination", is,
+ "Not a Valid JPEG File");
+ length--;
+ int precision = (precisionAndDestination >> 4) & 0xf;
+ int destinationIdentifier = precisionAndDestination & 0xf;
+
+ int[] elements = new int[64];
+ for (int i = 0; i < 64; i++)
+ {
+ if (precision == 0)
+ {
+ elements[i] = 0xff & readByte("QuantizationTableElement",
+ is, "Not a Valid JPEG File");
+ length--;
+ }
+ else if (precision == 1)
+ {
+ elements[i] = read2Bytes("QuantizationTableElement",
+ is, "Not a Valid JPEG File");
+ length -= 2;
+ }
+ else
+ {
+ throw new ImageReadException("Quantization table precision '" +
+ precision + "' is invalid");
+ }
+ }
+
+ quantizationTables.add(new QuantizationTable(
+ precision, destinationIdentifier, elements));
+ }
+ }
+
+ public String getDescription()
+ {
+ return "DQT (" + getSegmentType() + ")";
+ }
+}
Propchange: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/DQTSegment.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/SOFNSegment.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/SOFNSegment.java?rev=1090754&r1=1090753&r2=1090754&view=diff
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/SOFNSegment.java (original)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/SOFNSegment.java Sun Apr 10 09:08:42 2011
@@ -28,7 +28,27 @@ public class SOFNSegment extends Segment
public final int width, height;
public final int numberOfComponents;
public final int precision;
+ public final Component[] components;
+ public static class Component
+ {
+ public final int componentIdentifier;
+ public final int horizontalSamplingFactor;
+ public final int verticalSamplingFactor;
+ public final int quantTabDestSelector;
+
+ public Component(int componentIdentifier,
+ int horizontalSamplingFactor,
+ int veritcalSamplingFactor,
+ int quantTabDestSelector)
+ {
+ this.componentIdentifier = componentIdentifier;
+ this.horizontalSamplingFactor = horizontalSamplingFactor;
+ this.verticalSamplingFactor = veritcalSamplingFactor;
+ this.quantTabDestSelector = quantTabDestSelector;
+ }
+ }
+
public SOFNSegment(int marker, byte segmentData[])
throws ImageReadException, IOException
{
@@ -43,23 +63,26 @@ public class SOFNSegment extends Segment
if (getDebug())
System.out.println("SOF0Segment marker_length: " + marker_length);
+ precision = readByte("Data_precision", is, "Not a Valid JPEG File");
+ height = read2Bytes("Image_height", is, "Not a Valid JPEG File");
+ width = read2Bytes("Image_Width", is, "Not a Valid JPEG File");
+ numberOfComponents = readByte("Number_of_components", is,
+ "Not a Valid JPEG File");
+ components = new Component[numberOfComponents];
+ for (int i = 0; i < numberOfComponents; i++)
{
- precision = readByte("Data_precision", is, "Not a Valid JPEG File");
- height = read2Bytes("Image_height", is, "Not a Valid JPEG File");
- width = read2Bytes("Image_Width", is, "Not a Valid JPEG File");
- numberOfComponents = readByte("Number_of_components", is,
+ int componentIdentifier = readByte("ComponentIdentifier", is,
"Not a Valid JPEG File");
- // ignore the rest of the segment for now...
- skipBytes(is, marker_length - 6,
- "Not a Valid JPEG File: SOF0 Segment");
-
- // int Each_component1 = read_byte("Each_component1", is,
- // "Not a Valid JPEG File");
- // int Each_component2 = read_byte("Each_component2", is,
- // "Not a Valid JPEG File");
- // int Each_component3 = read_byte("Each_component3", is,
- // "Not a Valid JPEG File");
+ int hvSamplingFactors = readByte("SamplingFactors", is,
+ "Not a Valid JPEG File");
+ int horizontalSamplingFactor = (hvSamplingFactors >> 4) & 0xf;
+ int verticalSamplingFactor = hvSamplingFactors & 0xf;
+ int quantTabDestSelector = readByte("QuantTabDestSel", is,
+ "Not a Valid JPEG File");
+ components[i] = new Component(componentIdentifier,
+ horizontalSamplingFactor, verticalSamplingFactor,
+ quantTabDestSelector);
}
if (getDebug())
Modified: commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/SOSSegment.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/SOSSegment.java?rev=1090754&r1=1090753&r2=1090754&view=diff
==============================================================================
--- commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/SOSSegment.java (original)
+++ commons/proper/sanselan/trunk/src/main/java/org/apache/sanselan/formats/jpeg/segments/SOSSegment.java Sun Apr 10 09:08:42 2011
@@ -16,6 +16,7 @@
*/
package org.apache.sanselan.formats.jpeg.segments;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -24,12 +25,34 @@ import org.apache.sanselan.util.Debug;
public class SOSSegment extends Segment
{
- // public final int width, height;
- // public final int Number_of_components;
- // public final int Precision;
+ public final int numberOfComponents;
+ public final Component[] components;
+ public final int startOfSpectralSelection;
+ public final int endOfSpectralSelection;
+ public final int successiveApproximationBitHigh;
+ public final int successiveApproximationBitLow;
- // public final byte bytes[];
- // public final int cur_marker, num_markers;
+ public static class Component
+ {
+ public final int scanComponentSelector;
+ public final int dcCodingTableSelector;
+ public final int acCodingTableSelector;
+
+ public Component(int scanComponentSelector,
+ int dcCodingTableSelector,
+ int acCodingTableSelector)
+ {
+ this.scanComponentSelector = scanComponentSelector;
+ this.dcCodingTableSelector = dcCodingTableSelector;
+ this.acCodingTableSelector = acCodingTableSelector;
+ }
+ }
+
+ public SOSSegment(int marker, byte[] segmentData)
+ throws ImageReadException, IOException
+ {
+ this(marker, segmentData.length, new ByteArrayInputStream(segmentData));
+ }
public SOSSegment(int marker, int marker_length, InputStream is)
throws ImageReadException, IOException
@@ -39,54 +62,45 @@ public class SOSSegment extends Segment
if (getDebug())
System.out.println("SOSSegment marker_length: " + marker_length);
- Debug.debug("SOS", marker_length);
- // {
- int number_of_components_in_scan = readByte(
+// Debug.debug("SOS", marker_length);
+
+ numberOfComponents = readByte(
"number_of_components_in_scan", is, "Not a Valid JPEG File");
- Debug.debug("number_of_components_in_scan",
- number_of_components_in_scan);
+// Debug.debug("number_of_components_in_scan",
+// numberOfComponents);
- for (int i = 0; i < number_of_components_in_scan; i++)
+ components = new Component[numberOfComponents];
+ for (int i = 0; i < numberOfComponents; i++)
{
int scan_component_selector = readByte("scan_component_selector",
is, "Not a Valid JPEG File");
- Debug.debug("scan_component_selector", scan_component_selector);
+// Debug.debug("scan_component_selector", scan_component_selector);
- int ac_dc_entrooy_coding_table_selector = readByte(
+ int ac_dc_entropy_coding_table_selector = readByte(
"ac_dc_entrooy_coding_table_selector", is,
"Not a Valid JPEG File");
- Debug.debug("ac_dc_entrooy_coding_table_selector",
- ac_dc_entrooy_coding_table_selector);
+// Debug.debug("ac_dc_entrooy_coding_table_selector",
+// ac_dc_entropy_coding_table_selector);
+
+ int dcCodingTableSelector = (ac_dc_entropy_coding_table_selector >> 4) & 0xf;
+ int acCodingTableSelector = ac_dc_entropy_coding_table_selector & 0xf;
+ components[i] = new Component(scan_component_selector,
+ dcCodingTableSelector, acCodingTableSelector);
}
- int start_of_spectral_selection = readByte(
+ startOfSpectralSelection = readByte(
"start_of_spectral_selection", is, "Not a Valid JPEG File");
- Debug.debug("start_of_spectral_selection", start_of_spectral_selection);
- int end_of_spectral_selection = readByte("end_of_spectral_selection",
+// Debug.debug("start_of_spectral_selection", startOfSpectralSelection);
+ endOfSpectralSelection = readByte("end_of_spectral_selection",
is, "Not a Valid JPEG File");
- Debug.debug("end_of_spectral_selection", end_of_spectral_selection);
+// Debug.debug("end_of_spectral_selection", endOfSpectralSelection);
int successive_approximation_bit_position = readByte(
"successive_approximation_bit_position", is,
"Not a Valid JPEG File");
- Debug.debug("successive_approximation_bit_position",
- successive_approximation_bit_position);
-
- // height = read2Bytes("Image_height", is, "Not a Valid JPEG File");
- // width = read2Bytes("Image_Width", is, "Not a Valid JPEG File");
- // Number_of_components = read_byte("Number_of_components", is,
- // "Not a Valid JPEG File");
- //
- // // ignore the rest of the segment for now...
- // skipBytes(is, marker_length - 6,
- // "Not a Valid JPEG File: SOF0 Segment");
- //
- // // int Each_component1 = read_byte("Each_component1", is,
- // // "Not a Valid JPEG File");
- // // int Each_component2 = read_byte("Each_component2", is,
- // // "Not a Valid JPEG File");
- // // int Each_component3 = read_byte("Each_component3", is,
- // // "Not a Valid JPEG File");
- // }
+// Debug.debug("successive_approximation_bit_position",
+// successive_approximation_bit_position);
+ successiveApproximationBitHigh = (successive_approximation_bit_position >> 4) & 0xf;
+ successiveApproximationBitLow = successive_approximation_bit_position & 0xf;
if (getDebug())
System.out.println("");
Modified: commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/roundtrip/RoundtripTest.java
URL: http://svn.apache.org/viewvc/commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/roundtrip/RoundtripTest.java?rev=1090754&r1=1090753&r2=1090754&view=diff
==============================================================================
--- commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/roundtrip/RoundtripTest.java (original)
+++ commons/proper/sanselan/trunk/src/test/java/org/apache/sanselan/roundtrip/RoundtripTest.java Sun Apr 10 09:08:42 2011
@@ -70,7 +70,7 @@ public class RoundtripTest extends Sanse
COLOR_FULL_RGB, true), //
new FormatInfo(ImageFormat.IMAGE_FORMAT_TIFF, true, true,
COLOR_FULL_RGB, true), //
- new FormatInfo(ImageFormat.IMAGE_FORMAT_JPEG, false, false,
+ new FormatInfo(ImageFormat.IMAGE_FORMAT_JPEG, true, false,
COLOR_FULL_RGB, true), //
new FormatInfo(ImageFormat.IMAGE_FORMAT_BMP, true, true,
COLOR_FULL_RGB, true), //