0001: package net.myvietnam.mvncore.thirdparty;
0002:
0003: // Version 1.0a
0004: // Copyright (C) 1998, James R. Weeks and BioElectroMech.
0005: // Visit BioElectroMech at www.obrador.com. Email James@obrador.com.
0006:
0007: // See license.txt for details about the allowed used of this software.
0008: // This software is based in part on the work of the Independent JPEG Group.
0009: // See IJGreadme.txt for details about the Independent JPEG Group's license.
0010:
0011: // This encoder is inspired by the Java Jpeg encoder by Florian Raemy,
0012: // studwww.eurecom.fr/~raemy.
0013: // It borrows a great deal of code and structure from the Independent
0014: // Jpeg Group's Jpeg 6a library, Copyright Thomas G. Lane.
0015: // See license.txt for details.
0016:
0017: import java.awt.AWTException;
0018: import java.awt.Frame;
0019: import java.awt.Image;
0020: import java.awt.MediaTracker;
0021: import java.awt.image.PixelGrabber;
0022: import java.io.BufferedOutputStream;
0023: import java.io.IOException;
0024: import java.io.OutputStream;
0025: import java.util.Vector;
0026:
0027: /*
0028: * JpegEncoder - The JPEG main program which performs a jpeg compression of
0029: * an image.
0030: */
0031:
0032: public class JpegEncoder extends Frame {
0033: Thread runner;
0034: BufferedOutputStream outStream;
0035: Image image;
0036: JpegInfo JpegObj;
0037: Huffman Huf;
0038: DCT dct;
0039: int imageHeight, imageWidth;
0040: int Quality;
0041: int code;
0042: public static int[] jpegNaturalOrder = { 0, 1, 8, 16, 9, 2, 3, 10,
0043: 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41,
0044: 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50,
0045: 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38,
0046: 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63, };
0047:
0048: public JpegEncoder(Image image, int quality, OutputStream out) {
0049: MediaTracker tracker = new MediaTracker(this );
0050: tracker.addImage(image, 0);
0051: try {
0052: tracker.waitForID(0);
0053: } catch (InterruptedException e) {
0054: //TODO
0055: }
0056: /*
0057: * Quality of the image.
0058: * 0 to 100 and from bad image quality, high compression to good
0059: * image quality low compression
0060: */
0061: Quality = quality;
0062:
0063: /*
0064: * Getting picture information
0065: * It takes the Width, Height and RGB scans of the image.
0066: */
0067: JpegObj = new JpegInfo(image);
0068:
0069: imageHeight = JpegObj.imageHeight;
0070: imageWidth = JpegObj.imageWidth;
0071: outStream = new BufferedOutputStream(out);
0072: dct = new DCT(Quality);
0073: Huf = new Huffman(imageWidth, imageHeight);
0074: }
0075:
0076: public void setQuality(int quality) {
0077: dct = new DCT(quality);
0078: }
0079:
0080: public int getQuality() {
0081: return Quality;
0082: }
0083:
0084: public void Compress() {
0085: WriteHeaders(outStream);
0086: WriteCompressedData(outStream);
0087: WriteEOI(outStream);
0088: try {
0089: outStream.flush();
0090: } catch (IOException e) {
0091: //TODO
0092: System.out.println("IO Error: " + e.getMessage());
0093: }
0094: }
0095:
0096: public void WriteCompressedData(BufferedOutputStream outStream) {
0097: int i, j, r, c, a, b;
0098: int comp, xpos, ypos, xblockoffset, yblockoffset;
0099: float inputArray[][];
0100: float dctArray1[][] = new float[8][8];
0101: double dctArray2[][] = new double[8][8];
0102: int dctArray3[] = new int[8 * 8];
0103:
0104: /*
0105: * This method controls the compression of the image.
0106: * Starting at the upper left of the image, it compresses 8x8 blocks
0107: * of data until the entire image has been compressed.
0108: */
0109:
0110: int lastDCvalue[] = new int[JpegObj.NumberOfComponents];
0111: //int zeroArray[] = new int[64]; // initialized to hold all zeros
0112: //int Width = 0, Height = 0;
0113: //int nothing = 0, not;
0114: int MinBlockWidth, MinBlockHeight;
0115: // This initial setting of MinBlockWidth and MinBlockHeight is done to
0116: // ensure they start with values larger than will actually be the case.
0117: MinBlockWidth = ((imageWidth % 8 != 0) ? (int) (Math
0118: .floor(imageWidth / 8d) + 1) * 8 : imageWidth);
0119: MinBlockHeight = ((imageHeight % 8 != 0) ? (int) (Math
0120: .floor(imageHeight / 8d) + 1) * 8 : imageHeight);
0121: for (comp = 0; comp < JpegObj.NumberOfComponents; comp++) {
0122: MinBlockWidth = Math.min(MinBlockWidth,
0123: JpegObj.BlockWidth[comp]);
0124: MinBlockHeight = Math.min(MinBlockHeight,
0125: JpegObj.BlockHeight[comp]);
0126: }
0127: xpos = 0;
0128: for (r = 0; r < MinBlockHeight; r++) {
0129: for (c = 0; c < MinBlockWidth; c++) {
0130: xpos = c * 8;
0131: ypos = r * 8;
0132: for (comp = 0; comp < JpegObj.NumberOfComponents; comp++) {
0133: //Width = JpegObj.BlockWidth[comp];
0134: //Height = JpegObj.BlockHeight[comp];
0135: inputArray = JpegObj.Components[comp];
0136:
0137: for (i = 0; i < JpegObj.VsampFactor[comp]; i++) {
0138: for (j = 0; j < JpegObj.HsampFactor[comp]; j++) {
0139: xblockoffset = j * 8;
0140: yblockoffset = i * 8;
0141: for (a = 0; a < 8; a++) {
0142: for (b = 0; b < 8; b++) {
0143:
0144: // I believe this is where the dirty line at the bottom of the image is
0145: // coming from. I need to do a check here to make sure I'm not reading past
0146: // image data.
0147: // This seems to not be a big issue right now. (04/04/98)
0148:
0149: dctArray1[a][b] = inputArray[ypos
0150: + yblockoffset + a][xpos
0151: + xblockoffset + b];
0152: }
0153: }
0154: // The following code commented out because on some images this technique
0155: // results in poor right and bottom borders.
0156: // if ((!JpegObj.lastColumnIsDummy[comp] || c < Width - 1) && (!JpegObj.lastRowIsDummy[comp] || r < Height - 1)) {
0157: dctArray2 = dct.forwardDCT(dctArray1);
0158: dctArray3 = dct.quantizeBlock(dctArray2,
0159: JpegObj.QtableNumber[comp]);
0160: // }
0161: // else {
0162: // zeroArray[0] = dctArray3[0];
0163: // zeroArray[0] = lastDCvalue[comp];
0164: // dctArray3 = zeroArray;
0165: // }
0166: Huf.HuffmanBlockEncoder(outStream,
0167: dctArray3, lastDCvalue[comp],
0168: JpegObj.DCtableNumber[comp],
0169: JpegObj.ACtableNumber[comp]);
0170: lastDCvalue[comp] = dctArray3[0];
0171: }
0172: }
0173: }
0174: }
0175: }
0176: Huf.flushBuffer(outStream);
0177: }
0178:
0179: public void WriteEOI(BufferedOutputStream out) {
0180: byte[] EOI = { (byte) 0xFF, (byte) 0xD9 };
0181: WriteMarker(EOI, out);
0182: }
0183:
0184: public void WriteHeaders(BufferedOutputStream out) {
0185: int i, j, index, offset, length;
0186: int tempArray[];
0187:
0188: // the SOI marker
0189: byte[] SOI = { (byte) 0xFF, (byte) 0xD8 };
0190: WriteMarker(SOI, out);
0191:
0192: // The order of the following headers is quiet inconsequential.
0193: // the JFIF header
0194: byte JFIF[] = new byte[18];
0195: JFIF[0] = (byte) 0xff;
0196: JFIF[1] = (byte) 0xe0;
0197: JFIF[2] = (byte) 0x00;
0198: JFIF[3] = (byte) 0x10;
0199: JFIF[4] = (byte) 0x4a;
0200: JFIF[5] = (byte) 0x46;
0201: JFIF[6] = (byte) 0x49;
0202: JFIF[7] = (byte) 0x46;
0203: JFIF[8] = (byte) 0x00;
0204: JFIF[9] = (byte) 0x01;
0205: JFIF[10] = (byte) 0x00;
0206: JFIF[11] = (byte) 0x00;
0207: JFIF[12] = (byte) 0x00;
0208: JFIF[13] = (byte) 0x01;
0209: JFIF[14] = (byte) 0x00;
0210: JFIF[15] = (byte) 0x01;
0211: JFIF[16] = (byte) 0x00;
0212: JFIF[17] = (byte) 0x00;
0213: WriteArray(JFIF, out);
0214:
0215: // Comment Header
0216: String comment = new String();
0217: comment = JpegObj.getComment();
0218: length = comment.length();
0219: byte COM[] = new byte[length + 4];
0220: COM[0] = (byte) 0xFF;
0221: COM[1] = (byte) 0xFE;
0222: COM[2] = (byte) ((length >> 8) & 0xFF);
0223: COM[3] = (byte) (length & 0xFF);
0224: java.lang.System.arraycopy(JpegObj.Comment.getBytes(), 0, COM,
0225: 4, JpegObj.Comment.length());
0226: WriteArray(COM, out);
0227:
0228: // The DQT header
0229: // 0 is the luminance index and 1 is the chrominance index
0230: byte DQT[] = new byte[134];
0231: DQT[0] = (byte) 0xFF;
0232: DQT[1] = (byte) 0xDB;
0233: DQT[2] = (byte) 0x00;
0234: DQT[3] = (byte) 0x84;
0235: offset = 4;
0236: for (i = 0; i < 2; i++) {
0237: DQT[offset++] = (byte) ((0 << 4) + i);
0238: tempArray = dct.quantum[i];
0239: for (j = 0; j < 64; j++) {
0240: DQT[offset++] = (byte) tempArray[jpegNaturalOrder[j]];
0241: }
0242: }
0243: WriteArray(DQT, out);
0244:
0245: // Start of Frame Header
0246: byte SOF[] = new byte[19];
0247: SOF[0] = (byte) 0xFF;
0248: SOF[1] = (byte) 0xC0;
0249: SOF[2] = (byte) 0x00;
0250: SOF[3] = (byte) 17;
0251: SOF[4] = (byte) JpegObj.Precision;
0252: SOF[5] = (byte) ((JpegObj.imageHeight >> 8) & 0xFF);
0253: SOF[6] = (byte) ((JpegObj.imageHeight) & 0xFF);
0254: SOF[7] = (byte) ((JpegObj.imageWidth >> 8) & 0xFF);
0255: SOF[8] = (byte) ((JpegObj.imageWidth) & 0xFF);
0256: SOF[9] = (byte) JpegObj.NumberOfComponents;
0257: index = 10;
0258: for (i = 0; i < SOF[9]; i++) {
0259: SOF[index++] = (byte) JpegObj.CompID[i];
0260: SOF[index++] = (byte) ((JpegObj.HsampFactor[i] << 4) + JpegObj.VsampFactor[i]);
0261: SOF[index++] = (byte) JpegObj.QtableNumber[i];
0262: }
0263: WriteArray(SOF, out);
0264:
0265: // The DHT Header
0266: byte DHT1[], DHT2[], DHT3[], DHT4[];
0267: int bytes, temp, oldindex, intermediateindex;
0268: length = 2;
0269: index = 4;
0270: oldindex = 4;
0271: DHT1 = new byte[17];
0272: DHT4 = new byte[4];
0273: DHT4[0] = (byte) 0xFF;
0274: DHT4[1] = (byte) 0xC4;
0275: for (i = 0; i < 4; i++) {
0276: bytes = 0;
0277: DHT1[index++ - oldindex] = (byte) ((int[]) Huf.bits
0278: .elementAt(i))[0];
0279: for (j = 1; j < 17; j++) {
0280: temp = ((int[]) Huf.bits.elementAt(i))[j];
0281: DHT1[index++ - oldindex] = (byte) temp;
0282: bytes += temp;
0283: }
0284: intermediateindex = index;
0285: DHT2 = new byte[bytes];
0286: for (j = 0; j < bytes; j++) {
0287: DHT2[index++ - intermediateindex] = (byte) ((int[]) Huf.val
0288: .elementAt(i))[j];
0289: }
0290: DHT3 = new byte[index];
0291: java.lang.System.arraycopy(DHT4, 0, DHT3, 0, oldindex);
0292: java.lang.System.arraycopy(DHT1, 0, DHT3, oldindex, 17);
0293: java.lang.System.arraycopy(DHT2, 0, DHT3, oldindex + 17,
0294: bytes);
0295: DHT4 = DHT3;
0296: oldindex = index;
0297: }
0298: DHT4[2] = (byte) (((index - 2) >> 8) & 0xFF);
0299: DHT4[3] = (byte) ((index - 2) & 0xFF);
0300: WriteArray(DHT4, out);
0301:
0302: // Start of Scan Header
0303: byte SOS[] = new byte[14];
0304: SOS[0] = (byte) 0xFF;
0305: SOS[1] = (byte) 0xDA;
0306: SOS[2] = (byte) 0x00;
0307: SOS[3] = (byte) 12;
0308: SOS[4] = (byte) JpegObj.NumberOfComponents;
0309: index = 5;
0310: for (i = 0; i < SOS[4]; i++) {
0311: SOS[index++] = (byte) JpegObj.CompID[i];
0312: SOS[index++] = (byte) ((JpegObj.DCtableNumber[i] << 4) + JpegObj.ACtableNumber[i]);
0313: }
0314: SOS[index++] = (byte) JpegObj.Ss;
0315: SOS[index++] = (byte) JpegObj.Se;
0316: SOS[index++] = (byte) ((JpegObj.Ah << 4) + JpegObj.Al);
0317: WriteArray(SOS, out);
0318:
0319: }
0320:
0321: void WriteMarker(byte[] data, BufferedOutputStream out) {
0322: try {
0323: out.write(data, 0, 2);
0324: } catch (IOException e) {
0325: //TODO
0326: System.out.println("IO Error: " + e.getMessage());
0327: }
0328: }
0329:
0330: void WriteArray(byte[] data, BufferedOutputStream out) {
0331: int length;
0332: try {
0333: length = ((data[2] & 0xFF) << 8) + (data[3] & 0xFF) + 2;
0334: out.write(data, 0, length);
0335: } catch (IOException e) {
0336: //TODO
0337: System.out.println("IO Error: " + e.getMessage());
0338: }
0339: }
0340: }
0341:
0342: // This class incorporates quality scaling as implemented in the JPEG-6a
0343: // library.
0344:
0345: /*
0346: * DCT - A Java implementation of the Discreet Cosine Transform
0347: */
0348:
0349: class DCT {
0350: /**
0351: * DCT Block Size - default 8
0352: */
0353: public int N = 8;
0354:
0355: /**
0356: * Image Quality (0-100) - default 80 (good image / good compression)
0357: */
0358: public int QUALITY = 80;
0359:
0360: public int quantum[][] = new int[2][];
0361: public double Divisors[][] = new double[2][];
0362:
0363: /**
0364: * Quantitization Matrix for luminace.
0365: */
0366: public int quantum_luminance[] = new int[N * N];
0367: public double DivisorsLuminance[] = new double[N * N];
0368:
0369: /**
0370: * Quantitization Matrix for chrominance.
0371: */
0372: public int quantum_chrominance[] = new int[N * N];
0373: public double DivisorsChrominance[] = new double[N * N];
0374:
0375: /**
0376: * Constructs a new DCT object. Initializes the cosine transform matrix
0377: * these are used when computing the DCT and it's inverse. This also
0378: * initializes the run length counters and the ZigZag sequence. Note that
0379: * the image quality can be worse than 25 however the image will be
0380: * extemely pixelated, usually to a block size of N.
0381: *
0382: * @param QUALITY The quality of the image (0 worst - 100 best)
0383: *
0384: */
0385: public DCT(int QUALITY) {
0386: initMatrix(QUALITY);
0387: }
0388:
0389: /*
0390: * This method sets up the quantization matrix for luminance and
0391: * chrominance using the Quality parameter.
0392: */
0393: private void initMatrix(int quality) {
0394: double[] AANscaleFactor = { 1.0, 1.387039845, 1.306562965,
0395: 1.175875602, 1.0, 0.785694958, 0.541196100, 0.275899379 };
0396: int i;
0397: int j;
0398: int index;
0399: int Quality;
0400: int temp;
0401:
0402: // converting quality setting to that specified in the jpeg_quality_scaling
0403: // method in the IJG Jpeg-6a C libraries
0404:
0405: Quality = quality;
0406: if (Quality <= 0)
0407: Quality = 1;
0408: if (Quality > 100)
0409: Quality = 100;
0410: if (Quality < 50)
0411: Quality = 5000 / Quality;
0412: else
0413: Quality = 200 - Quality * 2;
0414:
0415: // Creating the luminance matrix
0416:
0417: quantum_luminance[0] = 16;
0418: quantum_luminance[1] = 11;
0419: quantum_luminance[2] = 10;
0420: quantum_luminance[3] = 16;
0421: quantum_luminance[4] = 24;
0422: quantum_luminance[5] = 40;
0423: quantum_luminance[6] = 51;
0424: quantum_luminance[7] = 61;
0425: quantum_luminance[8] = 12;
0426: quantum_luminance[9] = 12;
0427: quantum_luminance[10] = 14;
0428: quantum_luminance[11] = 19;
0429: quantum_luminance[12] = 26;
0430: quantum_luminance[13] = 58;
0431: quantum_luminance[14] = 60;
0432: quantum_luminance[15] = 55;
0433: quantum_luminance[16] = 14;
0434: quantum_luminance[17] = 13;
0435: quantum_luminance[18] = 16;
0436: quantum_luminance[19] = 24;
0437: quantum_luminance[20] = 40;
0438: quantum_luminance[21] = 57;
0439: quantum_luminance[22] = 69;
0440: quantum_luminance[23] = 56;
0441: quantum_luminance[24] = 14;
0442: quantum_luminance[25] = 17;
0443: quantum_luminance[26] = 22;
0444: quantum_luminance[27] = 29;
0445: quantum_luminance[28] = 51;
0446: quantum_luminance[29] = 87;
0447: quantum_luminance[30] = 80;
0448: quantum_luminance[31] = 62;
0449: quantum_luminance[32] = 18;
0450: quantum_luminance[33] = 22;
0451: quantum_luminance[34] = 37;
0452: quantum_luminance[35] = 56;
0453: quantum_luminance[36] = 68;
0454: quantum_luminance[37] = 109;
0455: quantum_luminance[38] = 103;
0456: quantum_luminance[39] = 77;
0457: quantum_luminance[40] = 24;
0458: quantum_luminance[41] = 35;
0459: quantum_luminance[42] = 55;
0460: quantum_luminance[43] = 64;
0461: quantum_luminance[44] = 81;
0462: quantum_luminance[45] = 104;
0463: quantum_luminance[46] = 113;
0464: quantum_luminance[47] = 92;
0465: quantum_luminance[48] = 49;
0466: quantum_luminance[49] = 64;
0467: quantum_luminance[50] = 78;
0468: quantum_luminance[51] = 87;
0469: quantum_luminance[52] = 103;
0470: quantum_luminance[53] = 121;
0471: quantum_luminance[54] = 120;
0472: quantum_luminance[55] = 101;
0473: quantum_luminance[56] = 72;
0474: quantum_luminance[57] = 92;
0475: quantum_luminance[58] = 95;
0476: quantum_luminance[59] = 98;
0477: quantum_luminance[60] = 112;
0478: quantum_luminance[61] = 100;
0479: quantum_luminance[62] = 103;
0480: quantum_luminance[63] = 99;
0481:
0482: for (j = 0; j < 64; j++) {
0483: temp = (quantum_luminance[j] * Quality + 50) / 100;
0484: if (temp <= 0)
0485: temp = 1;
0486: if (temp > 255)
0487: temp = 255;
0488: quantum_luminance[j] = temp;
0489: }
0490: index = 0;
0491: for (i = 0; i < 8; i++) {
0492: for (j = 0; j < 8; j++) {
0493: // The divisors for the LL&M method (the slow integer method used in
0494: // jpeg 6a library). This method is currently (04/04/98) incompletely
0495: // implemented.
0496: // DivisorsLuminance[index] = ((double) quantum_luminance[index]) << 3;
0497: // The divisors for the AAN method (the float method used in jpeg 6a library.
0498: DivisorsLuminance[index] = 1d / (8d
0499: * quantum_luminance[index] * AANscaleFactor[i] * AANscaleFactor[j]);
0500: index++;
0501: }
0502: }
0503:
0504: // Creating the chrominance matrix
0505:
0506: quantum_chrominance[0] = 17;
0507: quantum_chrominance[1] = 18;
0508: quantum_chrominance[2] = 24;
0509: quantum_chrominance[3] = 47;
0510: quantum_chrominance[4] = 99;
0511: quantum_chrominance[5] = 99;
0512: quantum_chrominance[6] = 99;
0513: quantum_chrominance[7] = 99;
0514: quantum_chrominance[8] = 18;
0515: quantum_chrominance[9] = 21;
0516: quantum_chrominance[10] = 26;
0517: quantum_chrominance[11] = 66;
0518: quantum_chrominance[12] = 99;
0519: quantum_chrominance[13] = 99;
0520: quantum_chrominance[14] = 99;
0521: quantum_chrominance[15] = 99;
0522: quantum_chrominance[16] = 24;
0523: quantum_chrominance[17] = 26;
0524: quantum_chrominance[18] = 56;
0525: quantum_chrominance[19] = 99;
0526: quantum_chrominance[20] = 99;
0527: quantum_chrominance[21] = 99;
0528: quantum_chrominance[22] = 99;
0529: quantum_chrominance[23] = 99;
0530: quantum_chrominance[24] = 47;
0531: quantum_chrominance[25] = 66;
0532: quantum_chrominance[26] = 99;
0533: quantum_chrominance[27] = 99;
0534: quantum_chrominance[28] = 99;
0535: quantum_chrominance[29] = 99;
0536: quantum_chrominance[30] = 99;
0537: quantum_chrominance[31] = 99;
0538: quantum_chrominance[32] = 99;
0539: quantum_chrominance[33] = 99;
0540: quantum_chrominance[34] = 99;
0541: quantum_chrominance[35] = 99;
0542: quantum_chrominance[36] = 99;
0543: quantum_chrominance[37] = 99;
0544: quantum_chrominance[38] = 99;
0545: quantum_chrominance[39] = 99;
0546: quantum_chrominance[40] = 99;
0547: quantum_chrominance[41] = 99;
0548: quantum_chrominance[42] = 99;
0549: quantum_chrominance[43] = 99;
0550: quantum_chrominance[44] = 99;
0551: quantum_chrominance[45] = 99;
0552: quantum_chrominance[46] = 99;
0553: quantum_chrominance[47] = 99;
0554: quantum_chrominance[48] = 99;
0555: quantum_chrominance[49] = 99;
0556: quantum_chrominance[50] = 99;
0557: quantum_chrominance[51] = 99;
0558: quantum_chrominance[52] = 99;
0559: quantum_chrominance[53] = 99;
0560: quantum_chrominance[54] = 99;
0561: quantum_chrominance[55] = 99;
0562: quantum_chrominance[56] = 99;
0563: quantum_chrominance[57] = 99;
0564: quantum_chrominance[58] = 99;
0565: quantum_chrominance[59] = 99;
0566: quantum_chrominance[60] = 99;
0567: quantum_chrominance[61] = 99;
0568: quantum_chrominance[62] = 99;
0569: quantum_chrominance[63] = 99;
0570:
0571: for (j = 0; j < 64; j++) {
0572: temp = (quantum_chrominance[j] * Quality + 50) / 100;
0573: if (temp <= 0)
0574: temp = 1;
0575: if (temp >= 255)
0576: temp = 255;
0577: quantum_chrominance[j] = temp;
0578: }
0579: index = 0;
0580: for (i = 0; i < 8; i++) {
0581: for (j = 0; j < 8; j++) {
0582: // The divisors for the LL&M method (the slow integer method used in
0583: // jpeg 6a library). This method is currently (04/04/98) incompletely
0584: // implemented.
0585: // DivisorsChrominance[index] = ((double) quantum_chrominance[index]) << 3;
0586: // The divisors for the AAN method (the float method used in jpeg 6a library.
0587: DivisorsChrominance[index] = 1d / (8d
0588: * quantum_chrominance[index]
0589: * AANscaleFactor[i] * AANscaleFactor[j]);
0590: index++;
0591: }
0592: }
0593:
0594: // quantum and Divisors are objects used to hold the appropriate matices
0595:
0596: quantum[0] = quantum_luminance;
0597: Divisors[0] = DivisorsLuminance;
0598: quantum[1] = quantum_chrominance;
0599: Divisors[1] = DivisorsChrominance;
0600:
0601: }
0602:
0603: /*
0604: * This method preforms forward DCT on a block of image data using
0605: * the literal method specified for a 2-D Discrete Cosine Transform.
0606: * It is included as a curiosity and can give you an idea of the
0607: * difference in the compression result (the resulting image quality)
0608: * by comparing its output to the output of the AAN method below.
0609: * It is ridiculously inefficient.
0610: */
0611:
0612: // For now the final output is unusable. The associated quantization step
0613: // needs some tweaking. If you get this part working, please let me know.
0614: public double[][] forwardDCTExtreme(float input[][]) {
0615: double output[][] = new double[N][N];
0616: //double tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7;
0617: //double tmp10, tmp11, tmp12, tmp13;
0618: //double z1, z2, z3, z4, z5, z11, z13;
0619: //int i;
0620: //int j;
0621: int v, u, x, y;
0622: for (v = 0; v < 8; v++) {
0623: for (u = 0; u < 8; u++) {
0624: for (x = 0; x < 8; x++) {
0625: for (y = 0; y < 8; y++) {
0626: output[v][u] += input[x][y]
0627: * Math
0628: .cos(((2 * x + 1) * (double) u * Math.PI) / 16d)
0629: * Math
0630: .cos(((2 * y + 1) * (double) v * Math.PI) / 16d);
0631: }
0632: }
0633: output[v][u] *= 0.25d
0634: * ((u == 0) ? (1d / Math.sqrt(2)) : 1d)
0635: * ((v == 0) ? (1d / Math.sqrt(2)) : 1d);
0636: }
0637: }
0638: return output;
0639: }
0640:
0641: /*
0642: * This method preforms a DCT on a block of image data using the AAN
0643: * method as implemented in the IJG Jpeg-6a library.
0644: */
0645: public double[][] forwardDCT(float input[][]) {
0646: double output[][] = new double[N][N];
0647: double tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7;
0648: double tmp10, tmp11, tmp12, tmp13;
0649: double z1, z2, z3, z4, z5, z11, z13;
0650: int i;
0651: int j;
0652:
0653: // Subtracts 128 from the input values
0654: for (i = 0; i < 8; i++) {
0655: for (j = 0; j < 8; j++) {
0656: output[i][j] = input[i][j] - 128d;
0657: // input[i][j] -= 128;
0658:
0659: }
0660: }
0661:
0662: for (i = 0; i < 8; i++) {
0663: tmp0 = output[i][0] + output[i][7];
0664: tmp7 = output[i][0] - output[i][7];
0665: tmp1 = output[i][1] + output[i][6];
0666: tmp6 = output[i][1] - output[i][6];
0667: tmp2 = output[i][2] + output[i][5];
0668: tmp5 = output[i][2] - output[i][5];
0669: tmp3 = output[i][3] + output[i][4];
0670: tmp4 = output[i][3] - output[i][4];
0671:
0672: tmp10 = tmp0 + tmp3;
0673: tmp13 = tmp0 - tmp3;
0674: tmp11 = tmp1 + tmp2;
0675: tmp12 = tmp1 - tmp2;
0676:
0677: output[i][0] = tmp10 + tmp11;
0678: output[i][4] = tmp10 - tmp11;
0679:
0680: z1 = (tmp12 + tmp13) * 0.707106781d;
0681: output[i][2] = tmp13 + z1;
0682: output[i][6] = tmp13 - z1;
0683:
0684: tmp10 = tmp4 + tmp5;
0685: tmp11 = tmp5 + tmp6;
0686: tmp12 = tmp6 + tmp7;
0687:
0688: z5 = (tmp10 - tmp12) * 0.382683433d;
0689: z2 = 0.541196100d * tmp10 + z5;
0690: z4 = 1.306562965d * tmp12 + z5;
0691: z3 = tmp11 * 0.707106781d;
0692:
0693: z11 = tmp7 + z3;
0694: z13 = tmp7 - z3;
0695:
0696: output[i][5] = z13 + z2;
0697: output[i][3] = z13 - z2;
0698: output[i][1] = z11 + z4;
0699: output[i][7] = z11 - z4;
0700: }
0701:
0702: for (i = 0; i < 8; i++) {
0703: tmp0 = output[0][i] + output[7][i];
0704: tmp7 = output[0][i] - output[7][i];
0705: tmp1 = output[1][i] + output[6][i];
0706: tmp6 = output[1][i] - output[6][i];
0707: tmp2 = output[2][i] + output[5][i];
0708: tmp5 = output[2][i] - output[5][i];
0709: tmp3 = output[3][i] + output[4][i];
0710: tmp4 = output[3][i] - output[4][i];
0711:
0712: tmp10 = tmp0 + tmp3;
0713: tmp13 = tmp0 - tmp3;
0714: tmp11 = tmp1 + tmp2;
0715: tmp12 = tmp1 - tmp2;
0716:
0717: output[0][i] = tmp10 + tmp11;
0718: output[4][i] = tmp10 - tmp11;
0719:
0720: z1 = (tmp12 + tmp13) * 0.707106781d;
0721: output[2][i] = tmp13 + z1;
0722: output[6][i] = tmp13 - z1;
0723:
0724: tmp10 = tmp4 + tmp5;
0725: tmp11 = tmp5 + tmp6;
0726: tmp12 = tmp6 + tmp7;
0727:
0728: z5 = (tmp10 - tmp12) * 0.382683433d;
0729: z2 = 0.541196100d * tmp10 + z5;
0730: z4 = 1.306562965d * tmp12 + z5;
0731: z3 = tmp11 * 0.707106781d;
0732:
0733: z11 = tmp7 + z3;
0734: z13 = tmp7 - z3;
0735:
0736: output[5][i] = z13 + z2;
0737: output[3][i] = z13 - z2;
0738: output[1][i] = z11 + z4;
0739: output[7][i] = z11 - z4;
0740: }
0741:
0742: return output;
0743: }
0744:
0745: /*
0746: * This method quantitizes data and rounds it to the nearest integer.
0747: */
0748: public int[] quantizeBlock(double inputData[][], int code) {
0749: int outputData[] = new int[N * N];
0750: int i, j;
0751: int index;
0752: index = 0;
0753: for (i = 0; i < 8; i++) {
0754: for (j = 0; j < 8; j++) {
0755: // The second line results in significantly better compression.
0756: outputData[index] = (int) (Math.round(inputData[i][j]
0757: * Divisors[code][index]));
0758: // outputData[index] = (int)(((inputData[i][j] * (((double[]) (Divisors[code]))[index])) + 16384.5) -16384);
0759: index++;
0760: }
0761: }
0762:
0763: return outputData;
0764: }
0765:
0766: /*
0767: * This is the method for quantizing a block DCT'ed with forwardDCTExtreme
0768: * This method quantitizes data and rounds it to the nearest integer.
0769: */
0770: public int[] quantizeBlockExtreme(double inputData[][], int code) {
0771: int outputData[] = new int[N * N];
0772: int i, j;
0773: int index;
0774: index = 0;
0775: for (i = 0; i < 8; i++) {
0776: for (j = 0; j < 8; j++) {
0777: outputData[index] = (int) (Math.round(inputData[i][j]
0778: / quantum[code][index]));
0779: index++;
0780: }
0781: }
0782:
0783: return outputData;
0784: }
0785: }
0786:
0787: // This class was modified by James R. Weeks on 3/27/98.
0788: // It now incorporates Huffman table derivation as in the C jpeg library
0789: // from the IJG, Jpeg-6a.
0790:
0791: class Huffman {
0792: int bufferPutBits, bufferPutBuffer;
0793: public int ImageHeight;
0794: public int ImageWidth;
0795: public int DC_matrix0[][];
0796: public int AC_matrix0[][];
0797: public int DC_matrix1[][];
0798: public int AC_matrix1[][];
0799: public int DC_matrix[][][];
0800: public int AC_matrix[][][];
0801: public int code;
0802: public int NumOfDCTables;
0803: public int NumOfACTables;
0804: public int[] bitsDCluminance = { 0x00, 0, 1, 5, 1, 1, 1, 1, 1, 1,
0805: 0, 0, 0, 0, 0, 0, 0 };
0806: public int[] valDCluminance = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
0807: 11 };
0808: public int[] bitsDCchrominance = { 0x01, 0, 3, 1, 1, 1, 1, 1, 1, 1,
0809: 1, 1, 0, 0, 0, 0, 0 };
0810: public int[] valDCchrominance = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
0811: 11 };
0812: public int[] bitsACluminance = { 0x10, 0, 2, 1, 3, 3, 2, 4, 3, 5,
0813: 5, 4, 4, 0, 0, 1, 0x7d };
0814: public int[] valACluminance = { 0x01, 0x02, 0x03, 0x00, 0x04, 0x11,
0815: 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
0816: 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42,
0817: 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72,
0818: 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26,
0819: 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0820: 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53,
0821: 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65,
0822: 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77,
0823: 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
0824: 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a,
0825: 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2,
0826: 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
0827: 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
0828: 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4,
0829: 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4,
0830: 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa };
0831: public int[] bitsACchrominance = { 0x11, 0, 2, 1, 2, 4, 4, 3, 4, 7,
0832: 5, 4, 4, 0, 1, 2, 0x77 };
0833: public int[] valACchrominance = { 0x00, 0x01, 0x02, 0x03, 0x11,
0834: 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61,
0835: 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1,
0836: 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72,
0837: 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18,
0838: 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37,
0839: 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
0840: 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63,
0841: 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75,
0842: 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86,
0843: 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
0844: 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8,
0845: 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,
0846: 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,
0847: 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2,
0848: 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3,
0849: 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa };
0850: public Vector bits;
0851: public Vector val;
0852:
0853: /*
0854: * jpegNaturalOrder[i] is the natural-order position of the i'th element
0855: * of zigzag order.
0856: */
0857: public static int[] jpegNaturalOrder = { 0, 1, 8, 16, 9, 2, 3, 10,
0858: 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41,
0859: 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50,
0860: 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38,
0861: 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63, };
0862:
0863: /*
0864: * The Huffman class constructor
0865: */
0866: public Huffman(int Width, int Height) {
0867:
0868: bits = new Vector();
0869: bits.addElement(bitsDCluminance);
0870: bits.addElement(bitsACluminance);
0871: bits.addElement(bitsDCchrominance);
0872: bits.addElement(bitsACchrominance);
0873: val = new Vector();
0874: val.addElement(valDCluminance);
0875: val.addElement(valACluminance);
0876: val.addElement(valDCchrominance);
0877: val.addElement(valACchrominance);
0878: initHuf();
0879: //code=code;
0880: ImageWidth = Width;
0881: ImageHeight = Height;
0882:
0883: }
0884:
0885: /**
0886: * HuffmanBlockEncoder run length encodes and Huffman encodes the quantized
0887: * data.
0888: **/
0889:
0890: public void HuffmanBlockEncoder(BufferedOutputStream outStream,
0891: int zigzag[], int prec, int DCcode, int ACcode) {
0892: int temp, temp2, nbits, k, r, i;
0893:
0894: NumOfDCTables = 2;
0895: NumOfACTables = 2;
0896:
0897: // The DC portion
0898:
0899: temp = temp2 = zigzag[0] - prec;
0900: if (temp < 0) {
0901: temp = -temp;
0902: temp2--;
0903: }
0904: nbits = 0;
0905: while (temp != 0) {
0906: nbits++;
0907: temp >>= 1;
0908: }
0909: // if (nbits > 11) nbits = 11;
0910: bufferIt(outStream, DC_matrix[DCcode][nbits][0],
0911: DC_matrix[DCcode][nbits][1]);
0912: // The arguments in bufferIt are code and size.
0913: if (nbits != 0) {
0914: bufferIt(outStream, temp2, nbits);
0915: }
0916:
0917: // The AC portion
0918:
0919: r = 0;
0920:
0921: for (k = 1; k < 64; k++) {
0922: if ((temp = zigzag[jpegNaturalOrder[k]]) == 0) {
0923: r++;
0924: } else {
0925: while (r > 15) {
0926: bufferIt(outStream, AC_matrix[ACcode][0xF0][0],
0927: AC_matrix[ACcode][0xF0][1]);
0928: r -= 16;
0929: }
0930: temp2 = temp;
0931: if (temp < 0) {
0932: temp = -temp;
0933: temp2--;
0934: }
0935: nbits = 1;
0936: while ((temp >>= 1) != 0) {
0937: nbits++;
0938: }
0939: i = (r << 4) + nbits;
0940: bufferIt(outStream, AC_matrix[ACcode][i][0],
0941: AC_matrix[ACcode][i][1]);
0942: bufferIt(outStream, temp2, nbits);
0943:
0944: r = 0;
0945: }
0946: }
0947:
0948: if (r > 0) {
0949: bufferIt(outStream, AC_matrix[ACcode][0][0],
0950: AC_matrix[ACcode][0][1]);
0951: }
0952:
0953: }
0954:
0955: // Uses an integer long (32 bits) buffer to store the Huffman encoded bits
0956: // and sends them to outStream by the byte.
0957:
0958: void bufferIt(BufferedOutputStream outStream, int code, int size) {
0959: int PutBuffer = code;
0960: int PutBits = bufferPutBits;
0961:
0962: PutBuffer &= (1 << size) - 1;
0963: PutBits += size;
0964: PutBuffer <<= 24 - PutBits;
0965: PutBuffer |= bufferPutBuffer;
0966:
0967: while (PutBits >= 8) {
0968: int c = ((PutBuffer >> 16) & 0xFF);
0969: try {
0970: outStream.write(c);
0971: } catch (IOException e) {
0972: //TODO
0973: System.out.println("IO Error: " + e.getMessage());
0974: }
0975: if (c == 0xFF) {
0976: try {
0977: outStream.write(0);
0978: } catch (IOException e) {
0979: //TODO
0980: System.out.println("IO Error: " + e.getMessage());
0981: }
0982: }
0983: PutBuffer <<= 8;
0984: PutBits -= 8;
0985: }
0986: bufferPutBuffer = PutBuffer;
0987: bufferPutBits = PutBits;
0988:
0989: }
0990:
0991: void flushBuffer(BufferedOutputStream outStream) {
0992: int PutBuffer = bufferPutBuffer;
0993: int PutBits = bufferPutBits;
0994: while (PutBits >= 8) {
0995: int c = ((PutBuffer >> 16) & 0xFF);
0996: try {
0997: outStream.write(c);
0998: } catch (IOException e) {
0999: //TODO
1000: System.out.println("IO Error: " + e.getMessage());
1001: }
1002: if (c == 0xFF) {
1003: try {
1004: outStream.write(0);
1005: } catch (IOException e) {
1006: //TODO
1007: System.out.println("IO Error: " + e.getMessage());
1008: }
1009: }
1010: PutBuffer <<= 8;
1011: PutBits -= 8;
1012: }
1013: if (PutBits > 0) {
1014: int c = ((PutBuffer >> 16) & 0xFF);
1015: try {
1016: outStream.write(c);
1017: } catch (IOException e) {
1018: //TODO
1019: System.out.println("IO Error: " + e.getMessage());
1020: }
1021: }
1022: }
1023:
1024: /*
1025: * Initialisation of the Huffman codes for Luminance and Chrominance.
1026: * This code results in the same tables created in the IJG Jpeg-6a
1027: * library.
1028: */
1029:
1030: public void initHuf() {
1031: DC_matrix0 = new int[12][2];
1032: DC_matrix1 = new int[12][2];
1033: AC_matrix0 = new int[255][2];
1034: AC_matrix1 = new int[255][2];
1035: DC_matrix = new int[2][][];
1036: AC_matrix = new int[2][][];
1037: int p, l, i, lastp, si, code;
1038: int[] huffsize = new int[257];
1039: int[] huffcode = new int[257];
1040:
1041: /*
1042: * init of the DC values for the chrominance
1043: * [][0] is the code [][1] is the number of bit
1044: */
1045:
1046: p = 0;
1047: for (l = 1; l <= 16; l++) {
1048: for (i = 1; i <= bitsDCchrominance[l]; i++) {
1049: huffsize[p++] = l;
1050: }
1051: }
1052: huffsize[p] = 0;
1053: lastp = p;
1054:
1055: code = 0;
1056: si = huffsize[0];
1057: p = 0;
1058: while (huffsize[p] != 0) {
1059: while (huffsize[p] == si) {
1060: huffcode[p++] = code;
1061: code++;
1062: }
1063: code <<= 1;
1064: si++;
1065: }
1066:
1067: for (p = 0; p < lastp; p++) {
1068: DC_matrix1[valDCchrominance[p]][0] = huffcode[p];
1069: DC_matrix1[valDCchrominance[p]][1] = huffsize[p];
1070: }
1071:
1072: /*
1073: * Init of the AC hufmann code for the chrominance
1074: * matrix [][][0] is the code & matrix[][][1] is the number of bit needed
1075: */
1076:
1077: p = 0;
1078: for (l = 1; l <= 16; l++) {
1079: for (i = 1; i <= bitsACchrominance[l]; i++) {
1080: huffsize[p++] = l;
1081: }
1082: }
1083: huffsize[p] = 0;
1084: lastp = p;
1085:
1086: code = 0;
1087: si = huffsize[0];
1088: p = 0;
1089: while (huffsize[p] != 0) {
1090: while (huffsize[p] == si) {
1091: huffcode[p++] = code;
1092: code++;
1093: }
1094: code <<= 1;
1095: si++;
1096: }
1097:
1098: for (p = 0; p < lastp; p++) {
1099: AC_matrix1[valACchrominance[p]][0] = huffcode[p];
1100: AC_matrix1[valACchrominance[p]][1] = huffsize[p];
1101: }
1102:
1103: /*
1104: * init of the DC values for the luminance
1105: * [][0] is the code [][1] is the number of bit
1106: */
1107: p = 0;
1108: for (l = 1; l <= 16; l++) {
1109: for (i = 1; i <= bitsDCluminance[l]; i++) {
1110: huffsize[p++] = l;
1111: }
1112: }
1113: huffsize[p] = 0;
1114: lastp = p;
1115:
1116: code = 0;
1117: si = huffsize[0];
1118: p = 0;
1119: while (huffsize[p] != 0) {
1120: while (huffsize[p] == si) {
1121: huffcode[p++] = code;
1122: code++;
1123: }
1124: code <<= 1;
1125: si++;
1126: }
1127:
1128: for (p = 0; p < lastp; p++) {
1129: DC_matrix0[valDCluminance[p]][0] = huffcode[p];
1130: DC_matrix0[valDCluminance[p]][1] = huffsize[p];
1131: }
1132:
1133: /*
1134: * Init of the AC hufmann code for luminance
1135: * matrix [][][0] is the code & matrix[][][1] is the number of bit
1136: */
1137:
1138: p = 0;
1139: for (l = 1; l <= 16; l++) {
1140: for (i = 1; i <= bitsACluminance[l]; i++) {
1141: huffsize[p++] = l;
1142: }
1143: }
1144: huffsize[p] = 0;
1145: lastp = p;
1146:
1147: code = 0;
1148: si = huffsize[0];
1149: p = 0;
1150: while (huffsize[p] != 0) {
1151: while (huffsize[p] == si) {
1152: huffcode[p++] = code;
1153: code++;
1154: }
1155: code <<= 1;
1156: si++;
1157: }
1158: for (int q = 0; q < lastp; q++) {
1159: AC_matrix0[valACluminance[q]][0] = huffcode[q];
1160: AC_matrix0[valACluminance[q]][1] = huffsize[q];
1161: }
1162:
1163: DC_matrix[0] = DC_matrix0;
1164: DC_matrix[1] = DC_matrix1;
1165: AC_matrix[0] = AC_matrix0;
1166: AC_matrix[1] = AC_matrix1;
1167: }
1168:
1169: }
1170:
1171: /*
1172: * JpegInfo - Given an image, sets default information about it and divides
1173: * it into its constituant components, downsizing those that need to be.
1174: */
1175:
1176: class JpegInfo {
1177: String Comment;
1178: public Image imageobj;
1179: public int imageHeight;
1180: public int imageWidth;
1181: public int BlockWidth[];
1182: public int BlockHeight[];
1183:
1184: // the following are set as the default
1185: public int Precision = 8;
1186: public int NumberOfComponents = 3;
1187: public float Components[][][];
1188: public int[] CompID = { 1, 2, 3 };
1189: public int[] HsampFactor = { 1, 1, 1 };
1190: public int[] VsampFactor = { 1, 1, 1 };
1191: public int[] QtableNumber = { 0, 1, 1 };
1192: public int[] DCtableNumber = { 0, 1, 1 };
1193: public int[] ACtableNumber = { 0, 1, 1 };
1194: public boolean[] lastColumnIsDummy = { false, false, false };
1195: public boolean[] lastRowIsDummy = { false, false, false };
1196: public int Ss = 0;
1197: public int Se = 63;
1198: public int Ah = 0;
1199: public int Al = 0;
1200: public int compWidth[], compHeight[];
1201: public int MaxHsampFactor;
1202: public int MaxVsampFactor;
1203:
1204: public JpegInfo(Image image) {
1205: Components = new float[NumberOfComponents][][];
1206: compWidth = new int[NumberOfComponents];
1207: compHeight = new int[NumberOfComponents];
1208: BlockWidth = new int[NumberOfComponents];
1209: BlockHeight = new int[NumberOfComponents];
1210: imageobj = image;
1211: imageWidth = image.getWidth(null);
1212: imageHeight = image.getHeight(null);
1213: Comment = "JPEG Encoder Copyright 1998, James R. Weeks and BioElectroMech. ";
1214: getYCCArray();
1215: }
1216:
1217: public void setComment(String comment) {
1218: Comment.concat(comment);
1219: }
1220:
1221: public String getComment() {
1222: return Comment;
1223: }
1224:
1225: /*
1226: * This method creates and fills three arrays, Y, Cb, and Cr using the
1227: * input image.
1228: */
1229:
1230: private void getYCCArray() {
1231: int values[] = new int[imageWidth * imageHeight];
1232: int r, g, b, y, x;
1233: // In order to minimize the chance that grabPixels will throw an exception
1234: // it may be necessary to grab some pixels every few scanlines and process
1235: // those before going for more. The time expense may be prohibitive.
1236: // However, for a situation where memory overhead is a concern, this may be
1237: // the only choice.
1238: PixelGrabber grabber = new PixelGrabber(imageobj.getSource(),
1239: 0, 0, imageWidth, imageHeight, values, 0, imageWidth);
1240: MaxHsampFactor = 1;
1241: MaxVsampFactor = 1;
1242: for (y = 0; y < NumberOfComponents; y++) {
1243: MaxHsampFactor = Math.max(MaxHsampFactor, HsampFactor[y]);
1244: MaxVsampFactor = Math.max(MaxVsampFactor, VsampFactor[y]);
1245: }
1246: for (y = 0; y < NumberOfComponents; y++) {
1247: compWidth[y] = (((imageWidth % 8 != 0) ? ((int) Math
1248: .ceil(imageWidth / 8d)) * 8 : imageWidth) / MaxHsampFactor)
1249: * HsampFactor[y];
1250: if (compWidth[y] != ((imageWidth / MaxHsampFactor) * HsampFactor[y])) {
1251: lastColumnIsDummy[y] = true;
1252: }
1253: // results in a multiple of 8 for compWidth
1254: // this will make the rest of the program fail for the unlikely
1255: // event that someone tries to compress an 16 x 16 pixel image
1256: // which would of course be worse than pointless
1257: BlockWidth[y] = (int) Math.ceil(compWidth[y] / 8d);
1258: compHeight[y] = (((imageHeight % 8 != 0) ? ((int) Math
1259: .ceil(imageHeight / 8d)) * 8 : imageHeight) / MaxVsampFactor)
1260: * VsampFactor[y];
1261: if (compHeight[y] != ((imageHeight / MaxVsampFactor) * VsampFactor[y])) {
1262: lastRowIsDummy[y] = true;
1263: }
1264: BlockHeight[y] = (int) Math.ceil(compHeight[y] / 8d);
1265: }
1266: try {
1267: if (grabber.grabPixels() != true) {
1268: try {
1269: throw new AWTException("Grabber returned false: "
1270: + grabber.status());
1271: } catch (Exception e) {
1272: //TODO
1273: }
1274: }
1275: } catch (InterruptedException e) {
1276: //TODO
1277: }
1278: float Y[][] = new float[compHeight[0]][compWidth[0]];
1279: float Cr1[][] = new float[compHeight[0]][compWidth[0]];
1280: float Cb1[][] = new float[compHeight[0]][compWidth[0]];
1281: //float Cb2[][] = new float[compHeight[1]][compWidth[1]];
1282: //float Cr2[][] = new float[compHeight[2]][compWidth[2]];
1283: int index = 0;
1284: for (y = 0; y < imageHeight; ++y) {
1285: for (x = 0; x < imageWidth; ++x) {
1286: r = ((values[index] >> 16) & 0xff);
1287: g = ((values[index] >> 8) & 0xff);
1288: b = (values[index] & 0xff);
1289:
1290: // The following three lines are a more correct color conversion but
1291: // the current conversion technique is sufficient and results in a higher
1292: // compression rate.
1293: // Y[y][x] = 16 + (float)(0.8588*(0.299 * (float)r + 0.587 * (float)g + 0.114 * (float)b ));
1294: // Cb1[y][x] = 128 + (float)(0.8784*(-0.16874 * (float)r - 0.33126 * (float)g + 0.5 * (float)b));
1295: // Cr1[y][x] = 128 + (float)(0.8784*(0.5 * (float)r - 0.41869 * (float)g - 0.08131 * (float)b));
1296: Y[y][x] = (float) (0.299 * r + 0.587 * g + 0.114 * b);
1297: Cb1[y][x] = 128 + (float) (-0.16874 * r - 0.33126 * g + 0.5 * b);
1298: Cr1[y][x] = 128 + (float) (0.5 * r - 0.41869 * g - 0.08131 * b);
1299: index++;
1300: }
1301: }
1302:
1303: // Need a way to set the H and V sample factors before allowing downsampling.
1304: // For now (04/04/98) downsampling must be hard coded.
1305: // Until a better downsampler is implemented, this will not be done.
1306: // Downsampling is currently supported. The downsampling method here
1307: // is a simple box filter.
1308:
1309: Components[0] = Y;
1310: // Cb2 = DownSample(Cb1, 1);
1311: Components[1] = Cb1;
1312: // Cr2 = DownSample(Cr1, 2);
1313: Components[2] = Cr1;
1314: }
1315:
1316: float[][] DownSample(float[][] C, int comp) {
1317: int inrow, incol;
1318: int outrow, outcol;
1319: float output[][];
1320: //int temp;
1321: int bias;
1322: inrow = 0;
1323: incol = 0;
1324: output = new float[compHeight[comp]][compWidth[comp]];
1325: for (outrow = 0; outrow < compHeight[comp]; outrow++) {
1326: bias = 1;
1327: for (outcol = 0; outcol < compWidth[comp]; outcol++) {
1328: output[outrow][outcol] = (C[inrow][incol++]
1329: + C[inrow++][incol--] + C[inrow][incol++]
1330: + C[inrow--][incol++] + bias) / 4f;
1331: bias ^= 3;
1332: }
1333: inrow += 2;
1334: incol = 0;
1335: }
1336: return output;
1337: }
1338: }
|