| /* JPEGComponent.java -- |
| Copyright (C) 2005 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| package gnu.javax.imageio.jpeg; |
| |
| import java.util.ArrayList; |
| import java.io.IOException; |
| import java.awt.image.WritableRaster; |
| |
| import javax.imageio.plugins.jpeg.JPEGHuffmanTable; |
| |
| /** |
| * This class holds the methods to decode and write a component information to |
| * a raster. |
| */ |
| public class JPEGComponent |
| { |
| public byte factorH, factorV, component_id, quant_id; |
| public int width = 0, height = 0, maxV = 0, maxH = 0; |
| public HuffmanTable ACTable; |
| public HuffmanTable DCTable; |
| public int[] quantizationTable; |
| public double previousDC = 0; |
| ArrayList data = new ArrayList(); |
| |
| /** |
| * Initializes the component |
| * |
| * @param id |
| * @param factorHorizontal |
| * @param factorVertical |
| * @param quantizationID |
| */ |
| public JPEGComponent(byte id, byte factorHorizontal, byte factorVertical, |
| byte quantizationID) |
| { |
| component_id = id; |
| factorH = factorHorizontal; |
| factorV = factorVertical; |
| quant_id = quantizationID; |
| } |
| |
| /** |
| * If a restart marker is found with too little of an MCU count (i.e. our |
| * Restart Interval is 63 and we have 61 we copy the last MCU until it's |
| * full) |
| * |
| * @param index |
| * @param length |
| */ |
| public void padMCU(int index, int length) |
| { |
| double[] src = (double[]) data.get(index - 1); |
| for (int i = 0; i < length; i++) |
| data.add(index, src); |
| } |
| |
| /** |
| * Reset the interval by setting the previous DC value |
| */ |
| public void resetInterval() |
| { |
| previousDC = 0; |
| } |
| |
| /** |
| * Run the Quantization backward method on all of the block data. |
| */ |
| public void quantitizeData() |
| { |
| for (int i = 0; i < data.size(); i++) |
| { |
| double[] mydata = (double[]) data.get(i); |
| for (int j = 0; j < mydata.length; j++) |
| mydata[j] *= quantizationTable[j]; |
| } |
| } |
| |
| public void setDCTable(JPEGHuffmanTable table) |
| { |
| DCTable = new HuffmanTable(table); |
| } |
| |
| public void setACTable(JPEGHuffmanTable table) |
| { |
| ACTable = new HuffmanTable(table); |
| } |
| |
| /** |
| * Run the Inverse DCT method on all of the block data |
| */ |
| public void idctData(DCT myDCT) |
| { |
| for (int i = 0; i < data.size(); i++) |
| data.add(i,myDCT.fast_idct(ZigZag.decode8x8_map((double[]) data.remove(i)))); |
| } |
| |
| /** |
| * This scales up the component size based on the factor size. This |
| * calculates everyting up automatically so it's simply ran at the end of |
| * the frame to normalize the size of all of the components. |
| */ |
| public void scaleByFactors() |
| { |
| int factorUpVertical = maxV / factorV; |
| int factorUpHorizontal = maxH / factorH; |
| |
| if (factorUpVertical > 1) |
| { |
| for (int i = 0; i < data.size(); i++) |
| { |
| double[][] src = (double[][]) data.remove(i); |
| double[][] dest = |
| new double[src.length * factorUpVertical][src[0].length]; |
| for (int j = 0; j < src.length; j++) |
| { |
| for (int u = 0; u < factorUpVertical; u++) |
| { |
| dest[j * factorUpVertical + u] = src[j]; |
| } |
| } |
| data.add(i, dest); |
| } |
| } |
| |
| if (factorUpHorizontal > 1) |
| { |
| for (int i = 0; i < data.size(); i++) |
| { |
| double[][] src = (double[][]) data.remove(i); |
| double[][] dest = |
| new double[src.length][src[0].length * factorUpHorizontal]; |
| for (int j = 0; j < src.length; j++) |
| { |
| for (int u = 0; u < src[0].length; u++) |
| { |
| for (int v = 0; v < factorUpHorizontal; v++) |
| dest[j][u * factorUpHorizontal + v] = src[j][u]; |
| } |
| } |
| data.add(i, dest); |
| } |
| } |
| } |
| |
| /** |
| * This write the block of data to the raster throwing out anything that |
| * spills over the raster width or height. |
| * |
| * @param raster |
| * @param data |
| * @param compIndex |
| * @param x |
| * @param y |
| */ |
| public void writeBlock(WritableRaster raster, double[][] data, |
| int compIndex, int x, int y) |
| { |
| for (int yIndex = 0; yIndex < data.length; yIndex++) |
| { |
| for (int xIndex = 0; xIndex < data[yIndex].length; xIndex++) |
| { |
| // The if statement is needed because blocks can spill over the |
| // frame width because they are padded to make sure we keep the |
| // height of the block the same as the width of the block |
| if (x + xIndex < raster.getWidth() |
| && y + yIndex < raster.getHeight()) |
| raster.setSample(x + xIndex, y + yIndex, compIndex, |
| data[yIndex][xIndex]); |
| } |
| } |
| } |
| |
| /** |
| * This writes data to a raster block, so really it's reading not writing |
| * but it writes the data to the raster block by factor size in a zig zag |
| * fashion. This has the helper function writeBlock which does the actual |
| * writing. |
| * |
| * @param raster |
| * @param componentIndex |
| */ |
| public void writeData(WritableRaster raster, int componentIndex) |
| { |
| int x = 0, y = 0, lastblockheight = 0, incrementblock = 0; |
| |
| // Keep looping through all of the blocks until there are no more. |
| while(data.size() > 0) |
| { |
| int blockwidth = 0; |
| int blockheight = 0; |
| |
| if (x >= raster.getWidth()) |
| { |
| x = 0; |
| y += incrementblock; |
| } |
| |
| // Loop through the horizontal component blocks of the MCU first |
| // then for each horizontal line write out all of the vertical |
| // components |
| for (int factorVIndex = 0; factorVIndex < factorV; factorVIndex++) |
| { |
| blockwidth = 0; |
| |
| for (int factorHIndex = 0; factorHIndex < factorH; factorHIndex++) |
| { |
| // Captures the width of this block so we can increment the |
| // X coordinate |
| double[][] blockdata = (double[][]) data.remove(0); |
| |
| // Writes the data at the specific X and Y coordinate of |
| // this component |
| writeBlock(raster, blockdata, componentIndex, x, y); |
| blockwidth += blockdata[0].length; |
| x += blockdata[0].length; |
| blockheight = blockdata.length; |
| } |
| y += blockheight; |
| x -= blockwidth; |
| lastblockheight += blockheight; |
| } |
| y -= lastblockheight; |
| incrementblock = lastblockheight; |
| lastblockheight = 0; |
| x += blockwidth; |
| } |
| } |
| |
| /** |
| * Set the quantization table for this component. |
| * |
| * @param quanttable |
| */ |
| public void setQuantizationTable(int[] quanttable) |
| { |
| quantizationTable = quanttable; |
| } |
| |
| /** |
| * Read in a partial MCU for this component |
| * |
| * @param stream TODO |
| * @throws JPEGException TODO |
| * @throws IOException TODO |
| */ |
| public void readComponentMCU(JPEGImageInputStream stream) |
| throws JPEGException, IOException |
| { |
| for (int i = 0; i < factorH * factorV; i++) |
| { |
| double dc = decode_dc_coefficient(stream); |
| double[] datablock = decode_ac_coefficients(stream); |
| datablock[0] = dc; |
| data.add(datablock); |
| } |
| } |
| |
| /** |
| * Generated from text on F-22, F.2.2.1 - Huffman decoding of DC |
| * coefficients on ISO DIS 10918-1. Requirements and Guidelines. |
| * |
| * @param JPEGStream TODO |
| * |
| * @return TODO |
| * @throws JPEGException TODO |
| * @throws IOException TODO |
| */ |
| public double decode_dc_coefficient(JPEGImageInputStream JPEGStream) |
| throws JPEGException, IOException |
| { |
| int t = DCTable.decode(JPEGStream); |
| double diff = JPEGStream.readBits(t); |
| diff = HuffmanTable.extend((int) diff, t); |
| diff = (previousDC + diff); |
| previousDC = diff; |
| return diff; |
| } |
| |
| /** |
| * Generated from text on F-23, F.13 - Huffman decoded of AC coefficients |
| * on ISO DIS 10918-1. Requirements and Guidelines. |
| * |
| * @param JPEGStream TODO |
| * @return TODO |
| * |
| * @throws JPEGException TODO |
| * @throws IOException TODO |
| */ |
| public double[] decode_ac_coefficients(JPEGImageInputStream JPEGStream) |
| throws JPEGException, IOException |
| { |
| double[] zz = new double[64]; |
| |
| for (int k = 1; k < 64; k++) |
| { |
| int s = ACTable.decode(JPEGStream); |
| int r = s >> 4; |
| s &= 15; |
| |
| if (s != 0) |
| { |
| k += r; |
| r = (int) JPEGStream.readBits(s); |
| s = (int) HuffmanTable.extend(r, s); |
| zz[k] = s; |
| } |
| else |
| { |
| if (r != 15) |
| return (zz); |
| k += 15; |
| } |
| } |
| return zz; |
| } |
| } |