Source code: org/ydp/gfx/JpegEncoder.java
1 // Version 1.0a
2 // Copyright (C) 1998, James R. Weeks and BioElectroMech.
3 // Visit BioElectroMech at www.obrador.com. Email James@obrador.com.
4
5 // See license.txt for details about the allowed used of this software.
6 // This software is based in part on the work of the Independent JPEG Group.
7 // See IJGreadme.txt for details about the Independent JPEG Group's license.
8
9 // This encoder is inspired by the Java Jpeg encoder by Florian Raemy,
10 // studwww.eurecom.fr/~raemy.
11 // It borrows a great deal of code and structure from the Independent
12 // Jpeg Group's Jpeg 6a library, Copyright Thomas G. Lane.
13 // See license.txt for details.
14
15 package org.ydp.gfx;
16
17 import java.applet.Applet;
18 import java.awt.*;
19 import java.awt.image.*;
20 import java.io.*;
21 import java.util.*;
22 import java.lang.*;
23
24 /*
25 * JpegEncoder - The JPEG main program which performs a jpeg compression of
26 * an image.
27 */
28
29 public class JpegEncoder extends Frame
30 {
31 Thread runner;
32 BufferedOutputStream outStream;
33 Image image;
34 JpegInfo JpegObj;
35 Huffman Huf;
36 DCT dct;
37 int imageHeight, imageWidth;
38 int Quality;
39 int code;
40 public static int[] jpegNaturalOrder = {
41 0, 1, 8, 16, 9, 2, 3, 10,
42 17, 24, 32, 25, 18, 11, 4, 5,
43 12, 19, 26, 33, 40, 48, 41, 34,
44 27, 20, 13, 6, 7, 14, 21, 28,
45 35, 42, 49, 56, 57, 50, 43, 36,
46 29, 22, 15, 23, 30, 37, 44, 51,
47 58, 59, 52, 45, 38, 31, 39, 46,
48 53, 60, 61, 54, 47, 55, 62, 63,
49 };
50
51 public JpegEncoder(Image image, int quality, OutputStream out)
52 {
53 MediaTracker tracker = new MediaTracker(this);
54 tracker.addImage(image, 0);
55 try {
56 tracker.waitForID(0);
57 }
58 catch (InterruptedException e) {
59 // Got to do something?
60 }
61 /*
62 * Quality of the image.
63 * 0 to 100 and from bad image quality, high compression to good
64 * image quality low compression
65 */
66 Quality=quality;
67
68 /*
69 * Getting picture information
70 * It takes the Width, Height and RGB scans of the image.
71 */
72 JpegObj = new JpegInfo(image);
73
74 imageHeight=JpegObj.imageHeight;
75 imageWidth=JpegObj.imageWidth;
76 outStream = new BufferedOutputStream(out);
77 dct = new DCT(Quality);
78 Huf=new Huffman(imageWidth,imageHeight);
79 }
80
81 public void setQuality(int quality) {
82 dct = new DCT(quality);
83 }
84
85 public int getQuality() {
86 return Quality;
87 }
88
89 public void Compress() {
90 WriteHeaders(outStream);
91 WriteCompressedData(outStream);
92 WriteEOI(outStream);
93 try {
94 outStream.flush();
95 } catch (IOException e) {
96 System.out.println("IO Error: " + e.getMessage());
97 }
98 }
99
100 public void WriteCompressedData(BufferedOutputStream outStream) {
101 int offset, i, j, r, c,a ,b, temp = 0;
102 int comp, xpos, ypos, xblockoffset, yblockoffset;
103 float inputArray[][];
104 float dctArray1[][] = new float[8][8];
105 double dctArray2[][] = new double[8][8];
106 int dctArray3[] = new int[8*8];
107
108 /*
109 * This method controls the compression of the image.
110 * Starting at the upper left of the image, it compresses 8x8 blocks
111 * of data until the entire image has been compressed.
112 */
113
114 int lastDCvalue[] = new int[JpegObj.NumberOfComponents];
115 int zeroArray[] = new int[64]; // initialized to hold all zeros
116 int Width = 0, Height = 0;
117 int nothing = 0, not;
118 int MinBlockWidth, MinBlockHeight;
119 // This initial setting of MinBlockWidth and MinBlockHeight is done to
120 // ensure they start with values larger than will actually be the case.
121 MinBlockWidth = ((imageWidth%8 != 0) ? (int) (Math.floor((double) imageWidth/8.0) + 1)*8 : imageWidth);
122 MinBlockHeight = ((imageHeight%8 != 0) ? (int) (Math.floor((double) imageHeight/8.0) + 1)*8: imageHeight);
123 for (comp = 0; comp < JpegObj.NumberOfComponents; comp++) {
124 MinBlockWidth = Math.min(MinBlockWidth, JpegObj.BlockWidth[comp]);
125 MinBlockHeight = Math.min(MinBlockHeight, JpegObj.BlockHeight[comp]);
126 }
127 xpos = 0;
128 for (r = 0; r < MinBlockHeight; r++) {
129 for (c = 0; c < MinBlockWidth; c++) {
130 xpos = c*8;
131 ypos = r*8;
132 for (comp = 0; comp < JpegObj.NumberOfComponents; comp++) {
133 Width = JpegObj.BlockWidth[comp];
134 Height = JpegObj.BlockHeight[comp];
135 inputArray = (float[][]) JpegObj.Components[comp];
136
137 for(i = 0; i < JpegObj.VsampFactor[comp]; i++) {
138 for(j = 0; j < JpegObj.HsampFactor[comp]; j++) {
139 xblockoffset = j * 8;
140 yblockoffset = i * 8;
141 for (a = 0; a < 8; a++) {
142 for (b = 0; b < 8; b++) {
143
144 // I believe this is where the dirty line at the bottom of the image is
145 // coming from. I need to do a check here to make sure I'm not reading past
146 // image data.
147 // This seems to not be a big issue right now. (04/04/98)
148
149 dctArray1[a][b] = inputArray[ypos + yblockoffset + a][xpos + xblockoffset + b];
150 }
151 }
152 // The following code commented out because on some images this technique
153 // results in poor right and bottom borders.
154 // if ((!JpegObj.lastColumnIsDummy[comp] || c < Width - 1) && (!JpegObj.lastRowIsDummy[comp] || r < Height - 1)) {
155 dctArray2 = dct.forwardDCT(dctArray1);
156 dctArray3 = dct.quantizeBlock(dctArray2, JpegObj.QtableNumber[comp]);
157 // }
158 // else {
159 // zeroArray[0] = dctArray3[0];
160 // zeroArray[0] = lastDCvalue[comp];
161 // dctArray3 = zeroArray;
162 // }
163 Huf.HuffmanBlockEncoder(outStream, dctArray3, lastDCvalue[comp], JpegObj.DCtableNumber[comp], JpegObj.ACtableNumber[comp]);
164 lastDCvalue[comp] = dctArray3[0];
165 }
166 }
167 }
168 }
169 }
170 Huf.flushBuffer(outStream);
171 }
172
173 public void WriteEOI(BufferedOutputStream out) {
174 byte[] EOI = {(byte) 0xFF, (byte) 0xD9};
175 WriteMarker(EOI, out);
176 }
177
178 public void WriteHeaders(BufferedOutputStream out) {
179 int i, j, index, offset, length;
180 int tempArray[];
181
182 // the SOI marker
183 byte[] SOI = {(byte) 0xFF, (byte) 0xD8};
184 WriteMarker(SOI, out);
185
186 // The order of the following headers is quiet inconsequential.
187 // the JFIF header
188 byte JFIF[] = new byte[18];
189 JFIF[0] = (byte) 0xff;
190 JFIF[1] = (byte) 0xe0;
191 JFIF[2] = (byte) 0x00;
192 JFIF[3] = (byte) 0x10;
193 JFIF[4] = (byte) 0x4a;
194 JFIF[5] = (byte) 0x46;
195 JFIF[6] = (byte) 0x49;
196 JFIF[7] = (byte) 0x46;
197 JFIF[8] = (byte) 0x00;
198 JFIF[9] = (byte) 0x01;
199 JFIF[10] = (byte) 0x00;
200 JFIF[11] = (byte) 0x00;
201 JFIF[12] = (byte) 0x00;
202 JFIF[13] = (byte) 0x01;
203 JFIF[14] = (byte) 0x00;
204 JFIF[15] = (byte) 0x01;
205 JFIF[16] = (byte) 0x00;
206 JFIF[17] = (byte) 0x00;
207 WriteArray(JFIF, out);
208
209 // Comment Header
210 String comment = new String();
211 comment = JpegObj.getComment();
212 length = comment.length();
213 byte COM[] = new byte[length + 4];
214 COM[0] = (byte) 0xFF;
215 COM[1] = (byte) 0xFE;
216 COM[2] = (byte) ((length >> 8) & 0xFF);
217 COM[3] = (byte) (length & 0xFF);
218 java.lang.System.arraycopy(JpegObj.Comment.getBytes(), 0, COM, 4, JpegObj.Comment.length());
219 WriteArray(COM, out);
220
221 // The DQT header
222 // 0 is the luminance index and 1 is the chrominance index
223 byte DQT[] = new byte[134];
224 DQT[0] = (byte) 0xFF;
225 DQT[1] = (byte) 0xDB;
226 DQT[2] = (byte) 0x00;
227 DQT[3] = (byte) 0x84;
228 offset = 4;
229 for (i = 0; i < 2; i++) {
230 DQT[offset++] = (byte) ((0 << 4) + i);
231 tempArray = (int[]) dct.quantum[i];
232 for (j = 0; j < 64; j++) {
233 DQT[offset++] = (byte) tempArray[jpegNaturalOrder[j]];
234 }
235 }
236 WriteArray(DQT, out);
237
238 // Start of Frame Header
239 byte SOF[] = new byte[19];
240 SOF[0] = (byte) 0xFF;
241 SOF[1] = (byte) 0xC0;
242 SOF[2] = (byte) 0x00;
243 SOF[3] = (byte) 17;
244 SOF[4] = (byte) JpegObj.Precision;
245 SOF[5] = (byte) ((JpegObj.imageHeight >> 8) & 0xFF);
246 SOF[6] = (byte) ((JpegObj.imageHeight) & 0xFF);
247 SOF[7] = (byte) ((JpegObj.imageWidth >> 8) & 0xFF);
248 SOF[8] = (byte) ((JpegObj.imageWidth) & 0xFF);
249 SOF[9] = (byte) JpegObj.NumberOfComponents;
250 index = 10;
251 for (i = 0; i < SOF[9]; i++) {
252 SOF[index++] = (byte) JpegObj.CompID[i];
253 SOF[index++] = (byte) ((JpegObj.HsampFactor[i] << 4) + JpegObj.VsampFactor[i]);
254 SOF[index++] = (byte) JpegObj.QtableNumber[i];
255 }
256 WriteArray(SOF, out);
257
258 // The DHT Header
259 byte DHT1[], DHT2[], DHT3[], DHT4[];
260 int bytes, temp, oldindex, intermediateindex;
261 length = 2;
262 index = 4;
263 oldindex = 4;
264 DHT1 = new byte[17];
265 DHT4 = new byte[4];
266 DHT4[0] = (byte) 0xFF;
267 DHT4[1] = (byte) 0xC4;
268 for (i = 0; i < 4; i++ ) {
269 bytes = 0;
270 DHT1[index++ - oldindex] = (byte) ((int[]) Huf.bits.elementAt(i))[0];
271 for (j = 1; j < 17; j++) {
272 temp = ((int[]) Huf.bits.elementAt(i))[j];
273 DHT1[index++ - oldindex] =(byte) temp;
274 bytes += temp;
275 }
276 intermediateindex = index;
277 DHT2 = new byte[bytes];
278 for (j = 0; j < bytes; j++) {
279 DHT2[index++ - intermediateindex] = (byte) ((int[]) Huf.val.elementAt(i))[j];
280 }
281 DHT3 = new byte[index];
282 java.lang.System.arraycopy(DHT4, 0, DHT3, 0, oldindex);
283 java.lang.System.arraycopy(DHT1, 0, DHT3, oldindex, 17);
284 java.lang.System.arraycopy(DHT2, 0, DHT3, oldindex + 17, bytes);
285 DHT4 = DHT3;
286 oldindex = index;
287 }
288 DHT4[2] = (byte) (((index - 2) >> 8)& 0xFF);
289 DHT4[3] = (byte) ((index -2) & 0xFF);
290 WriteArray(DHT4, out);
291
292
293 // Start of Scan Header
294 byte SOS[] = new byte[14];
295 SOS[0] = (byte) 0xFF;
296 SOS[1] = (byte) 0xDA;
297 SOS[2] = (byte) 0x00;
298 SOS[3] = (byte) 12;
299 SOS[4] = (byte) JpegObj.NumberOfComponents;
300 index = 5;
301 for (i = 0; i < SOS[4]; i++) {
302 SOS[index++] = (byte) JpegObj.CompID[i];
303 SOS[index++] = (byte) ((JpegObj.DCtableNumber[i] << 4) + JpegObj.ACtableNumber[i]);
304 }
305 SOS[index++] = (byte) JpegObj.Ss;
306 SOS[index++] = (byte) JpegObj.Se;
307 SOS[index++] = (byte) ((JpegObj.Ah << 4) + JpegObj.Al);
308 WriteArray(SOS, out);
309
310 }
311
312 void WriteMarker(byte[] data, BufferedOutputStream out) {
313 try {
314 out.write(data, 0, 2);
315 } catch (IOException e) {
316 System.out.println("IO Error: " + e.getMessage());
317 }
318 }
319
320 void WriteArray(byte[] data, BufferedOutputStream out) {
321 int i, length;
322 try {
323 length = (((int) (data[2] & 0xFF)) << 8) + (int) (data[3] & 0xFF) + 2;
324 out.write(data, 0, length);
325 } catch (IOException e) {
326 System.out.println("IO Error: " + e.getMessage());
327 }
328 }
329 }
330
331 // This class incorporates quality scaling as implemented in the JPEG-6a
332 // library.
333
334 /*
335 * DCT - A Java implementation of the Discreet Cosine Transform
336 */
337
338 class DCT
339 {
340 /**
341 * DCT Block Size - default 8
342 */
343 public int N = 8;
344
345 /**
346 * Image Quality (0-100) - default 80 (good image / good compression)
347 */
348 public int QUALITY = 80;
349
350 public Object quantum[] = new Object[2];
351 public Object Divisors[] = new Object[2];
352
353 /**
354 * Quantitization Matrix for luminace.
355 */
356 public int quantum_luminance[] = new int[N*N];
357 public double DivisorsLuminance[] = new double[N*N];
358
359 /**
360 * Quantitization Matrix for chrominance.
361 */
362 public int quantum_chrominance[] = new int[N*N];
363 public double DivisorsChrominance[] = new double[N*N];
364
365 /**
366 * Constructs a new DCT object. Initializes the cosine transform matrix
367 * these are used when computing the DCT and it's inverse. This also
368 * initializes the run length counters and the ZigZag sequence. Note that
369 * the image quality can be worse than 25 however the image will be
370 * extemely pixelated, usually to a block size of N.
371 *
372 * @param QUALITY The quality of the image (0 worst - 100 best)
373 *
374 */
375 public DCT(int QUALITY)
376 {
377 initMatrix(QUALITY);
378 }
379
380
381 /*
382 * This method sets up the quantization matrix for luminance and
383 * chrominance using the Quality parameter.
384 */
385 private void initMatrix(int quality)
386 {
387 double[] AANscaleFactor = { 1.0, 1.387039845, 1.306562965, 1.175875602,
388 1.0, 0.785694958, 0.541196100, 0.275899379};
389 int i;
390 int j;
391 int index;
392 int Quality;
393 int temp;
394
395 // converting quality setting to that specified in the jpeg_quality_scaling
396 // method in the IJG Jpeg-6a C libraries
397
398 Quality = quality;
399 if (Quality <= 0)
400 Quality = 1;
401 if (Quality > 100)
402 Quality = 100;
403 if (Quality < 50)
404 Quality = 5000 / Quality;
405 else
406 Quality = 200 - Quality * 2;
407
408 // Creating the luminance matrix
409
410 quantum_luminance[0]=16;
411 quantum_luminance[1]=11;
412 quantum_luminance[2]=10;
413 quantum_luminance[3]=16;
414 quantum_luminance[4]=24;
415 quantum_luminance[5]=40;
416 quantum_luminance[6]=51;
417 quantum_luminance[7]=61;
418 quantum_luminance[8]=12;
419 quantum_luminance[9]=12;
420 quantum_luminance[10]=14;
421 quantum_luminance[11]=19;
422 quantum_luminance[12]=26;
423 quantum_luminance[13]=58;
424 quantum_luminance[14]=60;
425 quantum_luminance[15]=55;
426 quantum_luminance[16]=14;
427 quantum_luminance[17]=13;
428 quantum_luminance[18]=16;
429 quantum_luminance[19]=24;
430 quantum_luminance[20]=40;
431 quantum_luminance[21]=57;
432 quantum_luminance[22]=69;
433 quantum_luminance[23]=56;
434 quantum_luminance[24]=14;
435 quantum_luminance[25]=17;
436 quantum_luminance[26]=22;
437 quantum_luminance[27]=29;
438 quantum_luminance[28]=51;
439 quantum_luminance[29]=87;
440 quantum_luminance[30]=80;
441 quantum_luminance[31]=62;
442 quantum_luminance[32]=18;
443 quantum_luminance[33]=22;
444 quantum_luminance[34]=37;
445 quantum_luminance[35]=56;
446 quantum_luminance[36]=68;
447 quantum_luminance[37]=109;
448 quantum_luminance[38]=103;
449 quantum_luminance[39]=77;
450 quantum_luminance[40]=24;
451 quantum_luminance[41]=35;
452 quantum_luminance[42]=55;
453 quantum_luminance[43]=64;
454 quantum_luminance[44]=81;
455 quantum_luminance[45]=104;
456 quantum_luminance[46]=113;
457 quantum_luminance[47]=92;
458 quantum_luminance[48]=49;
459 quantum_luminance[49]=64;
460 quantum_luminance[50]=78;
461 quantum_luminance[51]=87;
462 quantum_luminance[52]=103;
463 quantum_luminance[53]=121;
464 quantum_luminance[54]=120;
465 quantum_luminance[55]=101;
466 quantum_luminance[56]=72;
467 quantum_luminance[57]=92;
468 quantum_luminance[58]=95;
469 quantum_luminance[59]=98;
470 quantum_luminance[60]=112;
471 quantum_luminance[61]=100;
472 quantum_luminance[62]=103;
473 quantum_luminance[63]=99;
474
475 for (j = 0; j < 64; j++)
476 {
477 temp = (quantum_luminance[j] * Quality + 50) / 100;
478 if ( temp <= 0) temp = 1;
479 if (temp > 255) temp = 255;
480 quantum_luminance[j] = temp;
481 }
482 index = 0;
483 for (i = 0; i < 8; i++) {
484 for (j = 0; j < 8; j++) {
485 // The divisors for the LL&M method (the slow integer method used in
486 // jpeg 6a library). This method is currently (04/04/98) incompletely
487 // implemented.
488 // DivisorsLuminance[index] = ((double) quantum_luminance[index]) << 3;
489 // The divisors for the AAN method (the float method used in jpeg 6a library.
490 DivisorsLuminance[index] = (double) ((double)1.0/((double) quantum_luminance[index] * AANscaleFactor[i] * AANscaleFactor[j] * (double) 8.0));
491 index++;
492 }
493 }
494
495
496 // Creating the chrominance matrix
497
498 quantum_chrominance[0]=17;
499 quantum_chrominance[1]=18;
500 quantum_chrominance[2]=24;
501 quantum_chrominance[3]=47;
502 quantum_chrominance[4]=99;
503 quantum_chrominance[5]=99;
504 quantum_chrominance[6]=99;
505 quantum_chrominance[7]=99;
506 quantum_chrominance[8]=18;
507 quantum_chrominance[9]=21;
508 quantum_chrominance[10]=26;
509 quantum_chrominance[11]=66;
510 quantum_chrominance[12]=99;
511 quantum_chrominance[13]=99;
512 quantum_chrominance[14]=99;
513 quantum_chrominance[15]=99;
514 quantum_chrominance[16]=24;
515 quantum_chrominance[17]=26;
516 quantum_chrominance[18]=56;
517 quantum_chrominance[19]=99;
518 quantum_chrominance[20]=99;
519 quantum_chrominance[21]=99;
520 quantum_chrominance[22]=99;
521 quantum_chrominance[23]=99;
522 quantum_chrominance[24]=47;
523 quantum_chrominance[25]=66;
524 quantum_chrominance[26]=99;
525 quantum_chrominance[27]=99;
526 quantum_chrominance[28]=99;
527 quantum_chrominance[29]=99;
528 quantum_chrominance[30]=99;
529 quantum_chrominance[31]=99;
530 quantum_chrominance[32]=99;
531 quantum_chrominance[33]=99;
532 quantum_chrominance[34]=99;
533 quantum_chrominance[35]=99;
534 quantum_chrominance[36]=99;
535 quantum_chrominance[37]=99;
536 quantum_chrominance[38]=99;
537 quantum_chrominance[39]=99;
538 quantum_chrominance[40]=99;
539 quantum_chrominance[41]=99;
540 quantum_chrominance[42]=99;
541 quantum_chrominance[43]=99;
542 quantum_chrominance[44]=99;
543 quantum_chrominance[45]=99;
544 quantum_chrominance[46]=99;
545 quantum_chrominance[47]=99;
546 quantum_chrominance[48]=99;
547 quantum_chrominance[49]=99;
548 quantum_chrominance[50]=99;
549 quantum_chrominance[51]=99;
550 quantum_chrominance[52]=99;
551 quantum_chrominance[53]=99;
552 quantum_chrominance[54]=99;
553 quantum_chrominance[55]=99;
554 quantum_chrominance[56]=99;
555 quantum_chrominance[57]=99;
556 quantum_chrominance[58]=99;
557 quantum_chrominance[59]=99;
558 quantum_chrominance[60]=99;
559 quantum_chrominance[61]=99;
560 quantum_chrominance[62]=99;
561 quantum_chrominance[63]=99;
562
563 for (j = 0; j < 64; j++)
564 {
565 temp = (quantum_chrominance[j] * Quality + 50) / 100;
566 if ( temp <= 0) temp = 1;
567 if (temp >= 255) temp = 255;
568 quantum_chrominance[j] = temp;
569 }
570 index = 0;
571 for (i = 0; i < 8; i++) {
572 for (j = 0; j < 8; j++) {
573 // The divisors for the LL&M method (the slow integer method used in
574 // jpeg 6a library). This method is currently (04/04/98) incompletely
575 // implemented.
576 // DivisorsChrominance[index] = ((double) quantum_chrominance[index]) << 3;
577 // The divisors for the AAN method (the float method used in jpeg 6a library.
578 DivisorsChrominance[index] = (double) ((double)1.0/((double) quantum_chrominance[index] * AANscaleFactor[i] * AANscaleFactor[j] * (double)8.0));
579 index++;
580 }
581 }
582
583 // quantum and Divisors are objects used to hold the appropriate matices
584
585 quantum[0] = quantum_luminance;
586 Divisors[0] = DivisorsLuminance;
587 quantum[1] = quantum_chrominance;
588 Divisors[1] = DivisorsChrominance;
589
590
591 }
592
593 /*
594 * This method preforms forward DCT on a block of image data using
595 * the literal method specified for a 2-D Discrete Cosine Transform.
596 * It is included as a curiosity and can give you an idea of the
597 * difference in the compression result (the resulting image quality)
598 * by comparing its output to the output of the AAN method below.
599 * It is ridiculously inefficient.
600 */
601
602 // For now the final output is unusable. The associated quantization step
603 // needs some tweaking. If you get this part working, please let me know.
604
605 public double[][] forwardDCTExtreme(float input[][])
606 {
607 double output[][] = new double[N][N];
608 double tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7;
609 double tmp10, tmp11, tmp12, tmp13;
610 double z1, z2, z3, z4, z5, z11, z13;
611 int i;
612 int j;
613 int v, u, x, y;
614 for (v = 0; v < 8; v++) {
615 for (u = 0; u < 8; u++) {
616 for (x = 0; x < 8; x++) {
617 for (y = 0; y < 8; y++) {
618 output[v][u] += ((double)input[x][y])*Math.cos(((double)(2*x + 1)*(double)u*Math.PI)/(double)16)*Math.cos(((double)(2*y + 1)*(double)v*Math.PI)/(double)16);
619 }
620 }
621 output[v][u] *= (double)(0.25)*((u == 0) ? ((double)1.0/Math.sqrt(2)) : (double) 1.0)*((v == 0) ? ((double)1.0/Math.sqrt(2)) : (double) 1.0);
622 }
623 }
624 return output;
625 }
626
627
628 /*
629 * This method preforms a DCT on a block of image data using the AAN
630 * method as implemented in the IJG Jpeg-6a library.
631 */
632 public double[][] forwardDCT(float input[][])
633 {
634 double output[][] = new double[N][N];
635 double tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7;
636 double tmp10, tmp11, tmp12, tmp13;
637 double z1, z2, z3, z4, z5, z11, z13;
638 int i;
639 int j;
640
641 // Subtracts 128 from the input values
642 for (i = 0; i < 8; i++) {
643 for(j = 0; j < 8; j++) {
644 output[i][j] = ((double)input[i][j] - (double)128.0);
645 // input[i][j] -= 128;
646
647 }
648 }
649
650 for (i = 0; i < 8; i++) {
651 tmp0 = output[i][0] + output[i][7];
652 tmp7 = output[i][0] - output[i][7];
653 tmp1 = output[i][1] + output[i][6];
654 tmp6 = output[i][1] - output[i][6];
655 tmp2 = output[i][2] + output[i][5];
656 tmp5 = output[i][2] - output[i][5];
657 tmp3 = output[i][3] + output[i][4];
658 tmp4 = output[i][3] - output[i][4];
659
660 tmp10 = tmp0 + tmp3;
661 tmp13 = tmp0 - tmp3;
662 tmp11 = tmp1 + tmp2;
663 tmp12 = tmp1 - tmp2;
664
665 output[i][0] = tmp10 + tmp11;
666 output[i][4] = tmp10 - tmp11;
667
668 z1 = (tmp12 + tmp13) * (double) 0.707106781;
669 output[i][2] = tmp13 + z1;
670 output[i][6] = tmp13 - z1;
671
672 tmp10 = tmp4 + tmp5;
673 tmp11 = tmp5 + tmp6;
674 tmp12 = tmp6 + tmp7;
675
676 z5 = (tmp10 - tmp12) * (double) 0.382683433;
677 z2 = ((double) 0.541196100) * tmp10 + z5;
678 z4 = ((double) 1.306562965) * tmp12 + z5;
679 z3 = tmp11 * ((double) 0.707106781);
680
681 z11 = tmp7 + z3;
682 z13 = tmp7 - z3;
683
684 output[i][5] = z13 + z2;
685 output[i][3] = z13 - z2;
686 output[i][1] = z11 + z4;
687 output[i][7] = z11 - z4;
688 }
689
690 for (i = 0; i < 8; i++) {
691 tmp0 = output[0][i] + output[7][i];
692 tmp7 = output[0][i] - output[7][i];
693 tmp1 = output[1][i] + output[6][i];
694 tmp6 = output[1][i] - output[6][i];
695 tmp2 = output[2][i] + output[5][i];
696 tmp5 = output[2][i] - output[5][i];
697 tmp3 = output[3][i] + output[4][i];
698 tmp4 = output[3][i] - output[4][i];
699
700 tmp10 = tmp0 + tmp3;
701 tmp13 = tmp0 - tmp3;
702 tmp11 = tmp1 + tmp2;
703 tmp12 = tmp1 - tmp2;
704
705 output[0][i] = tmp10 + tmp11;
706 output[4][i] = tmp10 - tmp11;
707
708 z1 = (tmp12 + tmp13) * (double) 0.707106781;
709 output[2][i] = tmp13 + z1;
710 output[6][i] = tmp13 - z1;
711
712 tmp10 = tmp4 + tmp5;
713 tmp11 = tmp5 + tmp6;
714 tmp12 = tmp6 + tmp7;
715
716 z5 = (tmp10 - tmp12) * (double) 0.382683433;
717 z2 = ((double) 0.541196100) * tmp10 + z5;
718 z4 = ((double) 1.306562965) * tmp12 + z5;
719 z3 = tmp11 * ((double) 0.707106781);
720
721 z11 = tmp7 + z3;
722 z13 = tmp7 - z3;
723
724 output[5][i] = z13 + z2;
725 output[3][i] = z13 - z2;
726 output[1][i] = z11 + z4;
727 output[7][i] = z11 - z4;
728 }
729
730 return output;
731 }
732
733 /*
734 * This method quantitizes data and rounds it to the nearest integer.
735 */
736 public int[] quantizeBlock(double inputData[][], int code)
737 {
738 int outputData[] = new int[N*N];
739 int i, j;
740 int index;
741 index = 0;
742 for (i = 0; i < 8; i++) {
743 for (j = 0; j < 8; j++) {
744 // The second line results in significantly better compression.
745 outputData[index] = (int)(Math.round(inputData[i][j] * (((double[]) (Divisors[code]))[index])));
746 // outputData[index] = (int)(((inputData[i][j] * (((double[]) (Divisors[code]))[index])) + 16384.5) -16384);
747 index++;
748 }
749 }
750
751 return outputData;
752 }
753
754 /*
755 * This is the method for quantizing a block DCT'ed with forwardDCTExtreme
756 * This method quantitizes data and rounds it to the nearest integer.
757 */
758 public int[] quantizeBlockExtreme(double inputData[][], int code)
759 {
760 int outputData[] = new int[N*N];
761 int i, j;
762 int index;
763 index = 0;
764 for (i = 0; i < 8; i++) {
765 for (j = 0; j < 8; j++) {
766 outputData[index] = (int)(Math.round(inputData[i][j] / (double)(((int[]) (quantum[code]))[index])));
767 index++;
768 }
769 }
770
771 return outputData;
772 }
773 }
774
775 // This class was modified by James R. Weeks on 3/27/98.
776 // It now incorporates Huffman table derivation as in the C jpeg library
777 // from the IJG, Jpeg-6a.
778
779 class Huffman
780 {
781 int bufferPutBits, bufferPutBuffer;
782 public int ImageHeight;
783 public int ImageWidth;
784 public int DC_matrix0[][];
785 public int AC_matrix0[][];
786 public int DC_matrix1[][];
787 public int AC_matrix1[][];
788 public Object DC_matrix[];
789 public Object AC_matrix[];
790 public int code;
791 public int NumOfDCTables;
792 public int NumOfACTables;
793 public int[] bitsDCluminance = { 0x00, 0, 1, 5, 1, 1,1,1,1,1,0,0,0,0,0,0,0};
794 public int[] valDCluminance = { 0,1,2,3,4,5,6,7,8,9,10,11 };
795 public int[] bitsDCchrominance = { 0x01,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0 };
796 public int[] valDCchrominance = { 0,1,2,3,4,5,6,7,8,9,10,11 };
797 public int[] bitsACluminance = {0x10,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d };
798 public int[] valACluminance =
799 { 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
800 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
801 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
802 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
803 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
804 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
805 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
806 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
807 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
808 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
809 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
810 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
811 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
812 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
813 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
814 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
815 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
816 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
817 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
818 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
819 0xf9, 0xfa };
820 public int[] bitsACchrominance = { 0x11,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77 };;
821 public int[] valACchrominance =
822 { 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
823 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
824 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
825 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
826 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
827 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
828 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
829 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
830 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
831 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
832 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
833 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
834 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
835 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
836 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
837 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
838 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
839 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
840 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
841 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
842 0xf9, 0xfa };
843 public Vector bits;
844 public Vector val;
845
846 /*
847 * jpegNaturalOrder[i] is the natural-order position of the i'th element
848 * of zigzag order.
849 */
850 public static int[] jpegNaturalOrder = {
851 0, 1, 8, 16, 9, 2, 3, 10,
852 17, 24, 32, 25, 18, 11, 4, 5,
853 12, 19, 26, 33, 40, 48, 41, 34,
854 27, 20, 13, 6, 7, 14, 21, 28,
855 35, 42, 49, 56, 57, 50, 43, 36,
856 29, 22, 15, 23, 30, 37, 44, 51,
857 58, 59, 52, 45, 38, 31, 39, 46,
858 53, 60, 61, 54, 47, 55, 62, 63,
859 };
860 /*
861 * The Huffman class constructor
862 */
863 public Huffman(int Width,int Height)
864 {
865
866 bits = new Vector();
867 bits.addElement(bitsDCluminance);
868 bits.addElement(bitsACluminance);
869 bits.addElement(bitsDCchrominance);
870 bits.addElement(bitsACchrominance);
871 val = new Vector();
872 val.addElement(valDCluminance);
873 val.addElement(valACluminance);
874 val.addElement(valDCchrominance);
875 val.addElement(valACchrominance);
876 initHuf();
877 code=code;
878 ImageWidth=Width;
879 ImageHeight=Height;
880
881 }
882
883 /**
884 * HuffmanBlockEncoder run length encodes and Huffman encodes the quantized
885 * data.
886 **/
887
888 public void HuffmanBlockEncoder(BufferedOutputStream outStream, int zigzag[], int prec, int DCcode, int ACcode)
889 {
890 int temp, temp2, nbits, k, r, i;
891
892 NumOfDCTables = 2;
893 NumOfACTables = 2;
894
895 // The DC portion
896
897 temp = temp2 = zigzag[0] - prec;
898 if(temp < 0) {
899 temp = -temp;
900 temp2--;
901 }
902 nbits = 0;
903 while (temp != 0) {
904 nbits++;
905 temp >>= 1;
906 }
907 // if (nbits > 11) nbits = 11;
908 bufferIt(outStream, ((int[][])DC_matrix[DCcode])[nbits][0], ((int[][])DC_matrix[DCcode])[nbits][1]);
909 // The arguments in bufferIt are code and size.
910 if (nbits != 0) {
911 bufferIt(outStream, temp2, nbits);
912 }
913
914 // The AC portion
915
916 r = 0;
917
918 for (k = 1; k < 64; k++) {
919 if ((temp = zigzag[jpegNaturalOrder[k]]) == 0) {
920 r++;
921 }
922 else {
923 while (r > 15) {
924 bufferIt(outStream, ((int[][])AC_matrix[ACcode])[0xF0][0], ((int[][])AC_matrix[ACcode])[0xF0][1]);
925 r -= 16;
926 }
927 temp2 = temp;
928 if (temp < 0) {
929 temp = -temp;
930 temp2--;
931 }
932 nbits = 1;
933 while ((temp >>= 1) != 0) {
934 nbits++;
935 }
936 i = (r << 4) + nbits;
937 bufferIt(outStream, ((int[][])AC_matrix[ACcode])[i][0], ((int[][])AC_matrix[ACcode])[i][1]);
938 bufferIt(outStream, temp2, nbits);
939
940 r = 0;
941 }
942 }
943
944 if (r > 0) {
945 bufferIt(outStream, ((int[][])AC_matrix[ACcode])[0][0], ((int[][])AC_matrix[ACcode])[0][1]);
946 }
947
948 }
949
950 // Uses an integer long (32 bits) buffer to store the Huffman encoded bits
951 // and sends them to outStream by the byte.
952
953 void bufferIt(BufferedOutputStream outStream, int code,int size)
954 {
955 int PutBuffer = code;
956 int PutBits = bufferPutBits;
957
958 PutBuffer &= (1 << size) - 1;
959 PutBits += size;
960 PutBuffer <<= 24 - PutBits;
961 PutBuffer |= bufferPutBuffer;
962
963 while(PutBits >= 8) {
964 int c = ((PutBuffer >> 16) & 0xFF);
965 try
966 {
967 outStream.write(c);
968 }
969 catch (IOException e) {
970 System.out.println("IO Error: " + e.getMessage());
971 }
972 if (c == 0xFF) {
973 try
974 {
975 outStream.write(0);
976 }
977 catch (IOException e) {
978 System.out.println("IO Error: " + e.getMessage());
979 }
980 }
981 PutBuffer <<= 8;
982 PutBits -= 8;
983 }
984 bufferPutBuffer = PutBuffer;
985 bufferPutBits = PutBits;
986
987 }
988
989 void flushBuffer(BufferedOutputStream outStream) {
990 int PutBuffer = bufferPutBuffer;
991 int PutBits = bufferPutBits;
992 while (PutBits >= 8) {
993 int c = ((PutBuffer >> 16) & 0xFF);
994 try
995 {
996 outStream.write(c);
997 }
998 catch (IOException e) {
999 System.out.println("IO Error: " + e.getMessage());
1000 }
1001 if (c == 0xFF) {
1002 try {
1003 outStream.write(0);
1004 }
1005 catch (IOException e) {
1006 System.out.println("IO Error: " + e.getMessage());
1007 }
1008 }
1009 PutBuffer <<= 8;
1010 PutBits -= 8;
1011 }
1012 if (PutBits > 0) {
1013 int c = ((PutBuffer >> 16) & 0xFF);
1014 try
1015 {
1016 outStream.write(c);
1017 }
1018 catch (IOException e) {
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 {
1032 DC_matrix0=new int[12][2];
1033 DC_matrix1=new int[12][2];
1034 AC_matrix0=new int[255][2];
1035 AC_matrix1=new int[255][2];
1036 DC_matrix = new Object[2];
1037 AC_matrix = new Object[2];
1038 int p, l, i, lastp, si, code;
1039 int[] huffsize = new int[257];
1040 int[] huffcode= new int[257];
1041
1042 /*
1043 * init of the DC values for the chrominance
1044 * [][0] is the code [][1] is the number of bit
1045 */
1046
1047 p = 0;
1048 for (l = 1; l <= 16; l++)
1049 {
1050 for (i = 1; i <= bitsDCchrominance[l]; i++)
1051 {
1052 huffsize[p++] = l;
1053 }
1054 }
1055 huffsize[p] = 0;
1056 lastp = p;
1057
1058 code = 0;
1059 si = huffsize[0];
1060 p = 0;
1061 while(huffsize[p] != 0)
1062 {
1063 while(huffsize[p] == si)
1064 {
1065 huffcode[p++] = code;
1066 code++;
1067 }
1068 code <<= 1;
1069 si++;
1070 }
1071
1072 for (p = 0; p < lastp; p++)
1073 {
1074 DC_matrix1[valDCchrominance[p]][0] = huffcode[p];
1075 DC_matrix1[valDCchrominance[p]][1] = huffsize[p];
1076 }
1077
1078 /*
1079 * Init of the AC hufmann code for the chrominance
1080 * matrix [][][0] is the code & matrix[][][1] is the number of bit needed
1081 */
1082
1083 p = 0;
1084 for (l = 1; l <= 16; l++)
1085 {
1086 for (i = 1; i <= bitsACchrominance[l]; i++)
1087 {
1088 huffsize[p++] = l;
1089 }
1090 }
1091 huffsize[p] = 0;
1092 lastp = p;
1093
1094 code = 0;
1095 si = huffsize[0];
1096 p = 0;
1097 while(huffsize[p] != 0)
1098 {
1099 while(huffsize[p] == si)
1100 {
1101 huffcode[p++] = code;
1102 code++;
1103 }
1104 code <<= 1;
1105 si++;
1106 }
1107
1108 for (p = 0; p < lastp; p++)
1109 {
1110 AC_matrix1[valACchrominance[p]][0] = huffcode[p];
1111 AC_matrix1[valACchrominance[p]][1] = huffsize[p];
1112 }
1113
1114 /*
1115 * init of the DC values for the luminance
1116 * [][0] is the code [][1] is the number of bit
1117 */
1118 p = 0;
1119 for (l = 1; l <= 16; l++)
1120 {
1121 for (i = 1; i <= bitsDCluminance[l]; i++)
1122 {
1123 huffsize[p++] = l;
1124 }
1125 }
1126 huffsize[p] = 0;
1127 lastp = p;
1128
1129 code = 0;
1130 si = huffsize[0];
1131 p = 0;
1132 while(huffsize[p] != 0)
1133 {
1134 while(huffsize[p] == si)
1135 {
1136 huffcode[p++] = code;
1137 code++;
1138 }
1139 code <<= 1;
1140 si++;
1141 }
1142
1143 for (p = 0; p < lastp; p++)
1144 {
1145 DC_matrix0[valDCluminance[p]][0] = huffcode[p];
1146 DC_matrix0[valDCluminance[p]][1] = huffsize[p];
1147 }
1148
1149 /*
1150 * Init of the AC hufmann code for luminance
1151 * matrix [][][0] is the code & matrix[][][1] is the number of bit
1152 */
1153
1154 p = 0;
1155 for (l = 1; l <= 16; l++)
1156 {
1157 for (i = 1; i <= bitsACluminance[l]; i++)
1158 {
1159 huffsize[p++] = l;
1160 }
1161 }
1162 huffsize[p] = 0;
1163 lastp = p;
1164
1165 code = 0;
1166 si = huffsize[0];
1167 p = 0;
1168 while(huffsize[p] != 0)
1169 {
1170 while(huffsize[p] == si)
1171 {
1172 huffcode[p++] = code;
1173 code++;
1174 }
1175 code <<= 1;
1176 si++;
1177 }
1178 for (int q = 0; q < lastp; q++)
1179 {
1180 AC_matrix0[valACluminance[q]][0] = huffcode[q];
1181 AC_matrix0[valACluminance[q]][1] = huffsize[q];
1182 }
1183
1184 DC_matrix[0] = DC_matrix0;
1185 DC_matrix[1] = DC_matrix1;
1186 AC_matrix[0] = AC_matrix0;
1187 AC_matrix[1] = AC_matrix1;
1188 }
1189
1190}
1191
1192/*
1193 * JpegInfo - Given an image, sets default information about it and divides
1194 * it into its constituant components, downsizing those that need to be.
1195 */
1196
1197class JpegInfo
1198{
1199 String Comment;
1200 public Image imageobj;
1201 public int imageHeight;
1202 public int imageWidth;
1203 public int BlockWidth[];
1204 public int BlockHeight[];
1205
1206// the following are set as the default
1207 public int Precision = 8;
1208 public int NumberOfComponents = 3;
1209 public Object Components[];
1210 public int[] CompID = {1, 2, 3};
1211 public int[] HsampFactor = {1, 1, 1};
1212 public int[] VsampFactor = {1, 1, 1};
1213 public int[] QtableNumber = {0, 1, 1};
1214 public int[] DCtableNumber = {0, 1, 1};
1215 public int[] ACtableNumber = {0, 1, 1};
1216 public boolean[] lastColumnIsDummy = {false, false, false};
1217 public boolean[] lastRowIsDummy = {false, false, false};
1218 public int Ss = 0;
1219 public int Se = 63;
1220 public int Ah = 0;
1221 public int Al = 0;
1222 public int compWidth[], compHeight[];
1223 public int MaxHsampFactor;
1224 public int MaxVsampFactor;
1225
1226
1227 public JpegInfo(Image image)
1228 {
1229 Components = new Object[NumberOfComponents];
1230 compWidth = new int[NumberOfComponents];
1231 compHeight = new int[NumberOfComponents];
1232 BlockWidth = new int[NumberOfComponents];
1233 BlockHeight = new int[NumberOfComponents];
1234 imageobj = image;
1235 imageWidth = image.getWidth(null);
1236 imageHeight = image.getHeight(null);
1237 Comment = "JPEG Encoder Copyright 1998, James R. Weeks and BioElectroMech. ";
1238 getYCCArray();
1239 }
1240
1241 public void setComment(String comment) {
1242 Comment.concat(comment);
1243 }
1244
1245 public String getComment() {
1246 return Comment;
1247 }
1248
1249 /*
1250 * This method creates and fills three arrays, Y, Cb, and Cr using the
1251 * input image.
1252 */
1253
1254 private void getYCCArray()
1255 {
1256 int values[] = new int[imageWidth * imageHeight];
1257 int r, g, b, y, x;
1258// In order to minimize the chance that grabPixels will throw an exception
1259// it may be necessary to grab some pixels every few scanlines and process
1260// those before going for more. The time expense may be prohibitive.
1261// However, for a situation where memory overhead is a concern, this may be
1262// the only choice.
1263 PixelGrabber grabber = new PixelGrabber(imageobj.getSource(), 0, 0, imageWidth, imageHeight, values, 0, imageWidth);
1264 MaxHsampFactor = 1;
1265 MaxVsampFactor = 1;
1266 for (y = 0; y < NumberOfComponents; y++) {
1267 MaxHsampFactor = Math.max(MaxHsampFactor, HsampFactor[y]);
1268 MaxVsampFactor = Math.max(MaxVsampFactor, VsampFactor[y]);
1269 }
1270 for (y = 0; y < NumberOfComponents; y++) {
1271 compWidth[y] = (((imageWidth%8 != 0) ? ((int) Math.ceil((double) imageWidth/8.0))*8 : imageWidth)/MaxHsampFactor)*HsampFactor[y];
1272 if (compWidth[y] != ((imageWidth/MaxHsampFactor)*HsampFactor[y])) {
1273 lastColumnIsDummy[y] = true;
1274 }
1275 // results in a multiple of 8 for compWidth
1276 // this will make the rest of the program fail for the unlikely
1277 // event that someone tries to compress an 16 x 16 pixel image
1278 // which would of course be worse than pointless
1279 BlockWidth[y] = (int) Math.ceil((double) compWidth[y]/8.0);
1280 compHeight[y] = (((imageHeight%8 != 0) ? ((int) Math.ceil((double) imageHeight/8.0))*8: imageHeight)/MaxVsampFactor)*VsampFactor[y];
1281 if (compHeight[y] != ((imageHeight/MaxVsampFactor)*VsampFactor[y])) {
1282 lastRowIsDummy[y] = true;
1283 }
1284 BlockHeight[y] = (int) Math.ceil((double) compHeight[y]/8.0);
1285 }
1286 try
1287 {
1288 if(grabber.grabPixels() != true)
1289 {
1290 try
1291 {
1292 throw new AWTException("Grabber returned false: " + grabber.status());
1293 }
1294 catch (Exception e) {};
1295 }
1296 }
1297 catch (InterruptedException e) {};
1298 float Y[][] = new float[compHeight[0]][compWidth[0]];
1299 float Cr1[][] = new float[compHeight[0]][compWidth[0]];
1300 float Cb1[][] = new float[compHeight[0]][compWidth[0]];
1301 float Cb2[][] = new float[compHeight[1]][compWidth[1]];
1302 float Cr2[][] = new float[compHeight[2]][compWidth[2]];
1303 int index = 0;
1304 for (y = 0; y < imageHeight; ++y)
1305 {
1306 for (x = 0; x < imageWidth; ++x)
1307 {
1308 r = ((values[index] >> 16) & 0xff);
1309 g = ((values[index] >> 8) & 0xff);
1310 b = (values[index] & 0xff);
1311
1312// The following three lines are a more correct color conversion but
1313// the current conversion technique is sufficient and results in a higher
1314// compression rate.
1315// Y[y][x] = 16 + (float)(0.8588*(0.299 * (float)r + 0.587 * (float)g + 0.114 * (float)b ));
1316// Cb1[y][x] = 128 + (float)(0.8784*(-0.16874 * (float)r - 0.33126 * (float)g + 0.5 * (float)b));
1317// Cr1[y][x] = 128 + (float)(0.8784*(0.5 * (float)r - 0.41869 * (float)g - 0.08131 * (float)b));
1318 Y[y][x] = (float)((0.299 * (float)r + 0.587 * (float)g + 0.114 * (float)b));
1319 Cb1[y][x] = 128 + (float)((-0.16874 * (float)r - 0.33126 * (float)g + 0.5 * (float)b));
1320 Cr1[y][x] = 128 + (float)((0.5 * (float)r - 0.41869 * (float)g - 0.08131 * (float)b));
1321 index++;
1322 }
1323 }
1324
1325// Need a way to set the H and V sample factors before allowing downsampling.
1326// For now (04/04/98) downsampling must be hard coded.
1327// Until a better downsampler is implemented, this will not be done.
1328// Downsampling is currently supported. The downsampling method here
1329// is a simple box filter.
1330
1331 Components[0] = Y;
1332// Cb2 = DownSample(Cb1, 1);
1333 Components[1] = Cb1;
1334// Cr2 = DownSample(Cr1, 2);
1335 Components[2] = Cr1;
1336 }
1337
1338 float[][] DownSample(float[][] C, int comp)
1339 {
1340 int inrow, incol;
1341 int outrow, outcol;
1342 float output[][];
1343 int temp;
1344 int bias;
1345 inrow = 0;
1346 incol = 0;
1347 output = new float[compHeight[comp]][compWidth[comp]];
1348 for (outrow = 0; outrow < compHeight[comp]; outrow++) {
1349 bias = 1;
1350 for (outcol = 0; outcol < compWidth[comp]; outcol++) {
1351 output[outrow][outcol] = (C[inrow][incol++] + C[inrow++][incol--] + C[inrow][incol++] + C[inrow--][incol++] + (float)bias)/(float)4.0;
1352 bias ^= 3;
1353 }
1354 inrow += 2;
1355 incol = 0;
1356 }
1357 return output;
1358 }
1359}