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), //