Source code: Acme/JPM/Encoders/GrayJPEG.java
1 //////////////////////// DISCLAIMER \\\\\\\\\\\\\\\\\\\\\\\\\
2 // \\
3 // This software is provided by Sean Patrick Breslin for \\
4 // your use or abuse, free of charge and royalties. This \\
5 // software is provide 'AS IS' and as such there are no \\
6 // warrenties expressed or implied. This software is by \\
7 // no means robust, optimized, efficient, tight or any of \\
8 // a number of adjectives used to describe software. This \\
9 // in mind, this software is not to be used for nuclear \\
10 // applications, world domination schemes or discount \\
11 // remote laser surgery. You may copy or modify this \\
12 // code as you see fit. This code works on my PC other \\
13 // than that I cannot help you with this code in any way. \\
14 // I have not decided whether or not I will support this \\
15 // code with bug fixes or enhancements but I'll think \\
16 // about it. \\
17 // \\
18 // USAGE: instanciate the class: \\
19 // GrayJPEG jpg = new GrayJPEG(); \\
20 // \\
21 // and call the method: \\
22 // jpg.compress(Image i, OutputStream os) \\
23 // \\
24 // Where 'i' is a standard Java Image object and \\
25 // 'os' is an output stream where you want the \\
26 // JPEG file written. \\
27 // \\
28 // NOTE: This code will only compress a grayscale image \\
29 // i.e. Black and White, though a color image will \\
30 // not crash the code the results are unknown. \\
31 // \\
32 // PS: I have compiled this with Java 1.0.2 and 1.1.3 \\
33 // and it works under both versions using windows \\
34 // 95 operating system, so knock yourself out and \\
35 // have fun with my code. \\
36 // \\
37 ///////////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
38 package Acme.JPM.Encoders;
39
40 import java.awt.*;
41 import java.awt.image.*;
42
43 import java.io.*;
44
45 import java.net.*;
46
47
48 /**
49 * DOCUMENT ME!
50 *
51 * @cvsversion $Revision: 1.2 $, $Date: 2003/10/16 08:49:27 $
52 */
53 public class GrayJPEG
54 {
55 //~ Instance fields ////////////////////////////////////////////////////////
56
57 private Image image;
58 private OutputStream fos;
59
60 // Header data
61 private String str = "JFIF Breslin Engineering JPEG Image Compression";
62 private byte[] APP0;
63 private byte[] BE;
64 private int[] BITS = new int[17];
65
66 // AC Huffman table
67 private byte[] Bits = {0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125};
68 private int[] EHUFCO = new int[257];
69 private int[] EHUFSI = new int[257];
70 private byte[] EOI = {-1, -39};
71 private int[] HUFFCODE = new int[257];
72 private int[] HUFFSIZE = new int[257];
73 private int[] HUFFVAL = new int[162];
74 private byte[] HuffACHeader = {-1, -60, 0, -75, 16};
75 private byte[] HuffDC =
76 {
77 -1, -60, 0, 31, 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1,
78 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
79 };
80 private byte[] Huffval =
81 {
82 1, 2, 3, 0, 4, 17, 5, 18, 33, 49, 65, 6, 19, 81, 97, 7, 34, 113, 20, 50,
83 -127, -111, -95, 8, 35, 66, -79, -63, 21, 82, -47, -16, 36, 51, 98, 114,
84 -126, 9, 10, 22, 23, 24, 25, 26, 37, 38, 39, 40, 41, 42, 52, 53, 54, 55,
85 56, 57, 58, 67, 68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87, 88, 89,
86 90, 99, 100, 101, 102, 103, 104, 105, 106, 115, 116, 117, 118, 119, 120,
87 121, 122, -125, -124, -123, -122, -121, -120, -119, -118, -110, -109,
88 -108, -107, -106, -105, -104, -103, -102, -94, -93, -92, -91, -90, -89,
89 -88, -87, -86, -78, -77, -76, -75, -74, -73, -72, -71, -70, -62, -61,
90 -60, -59, -58, -57, -56, -55, -54, -46, -45, -44, -43, -42, -41, -40,
91 -39, -38, -31, -30, -29, -28, -27, -26, -25, -24, -23, -22, -15, -14,
92 -13, -12, -11, -10, -9, -8, -7, -6
93 };
94
95 // quantization table in zigzag form for output file
96 private byte[] QNT =
97 {
98 -1, -37, 0, 67, 0, 13, 9, 10, 11, 10, 8, 13, 11, 10, 11, 14, 14, 13, 15,
99 19, 32, 21, 19, 18, 18, 19, 39, 28, 30, 23, 32, 46, 41, 49, 48, 46, 41,
100 45, 44, 51, 58, 74, 62, 51, 54, 70, 55, 44, 45, 64, 87, 65, 70, 76, 78,
101 82, 83, 82, 50, 62, 90, 97, 90, 80, 96, 74, 81, 82, 79
102 };
103
104 // table for doing quantization
105 private float[][] QT =
106 {
107 {13, 9, 8, 13, 19, 32, 41, 49},
108 {10, 10, 11, 15, 21, 46, 48, 44},
109 {11, 10, 13, 19, 32, 46, 55, 45},
110 {11, 14, 18, 23, 41, 70, 64, 50},
111 {14, 18, 30, 45, 54, 87, 82, 62},
112 {19, 28, 44, 51, 65, 83, 90, 74},
113 {39, 51, 62, 70, 82, 97, 96, 81},
114 {58, 74, 76, 78, 90, 80, 82, 79}
115 };
116 private byte[] SOF = {-1, -64, 0, 11, 8, 0, 0, 0, 0, 1, 1, 17, 0};
117 private byte[] SOI = {-1, -40};
118 private byte[] SOS = {-1, -38, 0, 8, 1, 1, 0, 0, 63, 0};
119
120 // Zig Zag array
121 private int[][] ZZ =
122 {
123 {0, 1, 5, 6, 14, 15, 27, 28},
124 {2, 4, 7, 13, 16, 26, 29, 42},
125 {3, 8, 12, 17, 25, 30, 41, 43},
126 {9, 11, 18, 24, 31, 40, 44, 53},
127 {10, 19, 23, 32, 39, 45, 52, 54},
128 {20, 22, 33, 38, 46, 51, 55, 60},
129 {21, 34, 37, 47, 50, 56, 59, 61},
130 {35, 36, 48, 49, 57, 58, 62, 63}
131 };
132 private int BitCnt;
133 private int CODE;
134
135 // Global variables
136 private int I;
137
138 // Global variables
139 private int J;
140 private int K;
141
142 // Global variables
143 private int LASTK;
144 private int R;
145 private int SI;
146 private int SSSS;
147
148 // Global variables
149 private int X;
150
151 // Global variables
152 private int Y;
153
154 // Global variables
155 private int ln;
156 private long DATA = 0;
157
158 //~ Constructors ///////////////////////////////////////////////////////////
159
160 public GrayJPEG()
161 {
162 int x;
163 int y;
164 int ln = str.length();
165 BE = new byte[ln];
166 str.getBytes(0, ln, BE, 0);
167 ln += 2;
168
169 byte[] ap = {-1, -32, 0, (byte) ln};
170 APP0 = ap;
171
172 // Generate AC Huffman tables
173 // Get BITS data
174 BITS[0] = 0;
175
176 for (x = 1; x < 17; x++)
177 {
178 BITS[x] = Bits[x - 1];
179 }
180
181 // Get HUFFVAL data
182 for (x = 0; x < 162; x++)
183 {
184 HUFFVAL[x] = Huffval[x];
185
186 if (HUFFVAL[x] < 0)
187 {
188 HUFFVAL[x] = 256 + HUFFVAL[x];
189 }
190 }
191
192 Generate_size_table();
193 Generate_code_table();
194 Order_codes();
195 }
196
197 //~ Methods ////////////////////////////////////////////////////////////////
198
199 public void compress(Image i, OutputStream os)
200 {
201 int bkk;
202 int x;
203 int y;
204 int w;
205 int h;
206 int a;
207 int b;
208 int c;
209 int d;
210 int[][] blocks;
211 int[][] blk = new int[8][8];
212
213 image = i;
214 fos = os;
215 X = image.getWidth(null);
216 Y = image.getHeight(null);
217 ln = X * Y;
218
219 int[] data = new int[ln];
220 w = (X >> 3) << 3;
221 h = (Y >> 3) << 3;
222 getPixels(data);
223
224 // Set Image dimensions in Start of Frame header
225 SOF[5] = (byte) ((h & 0x0000ff00) >> 8);
226 SOF[6] = (byte) (h & 0x000000ff);
227 SOF[7] = (byte) ((w & 0x0000ff00) >> 8);
228 SOF[8] = (byte) (w & 0x000000ff);
229
230 // Set number of blocks
231 w = X / 8;
232 h = Y / 8;
233 ln = w * h;
234 blocks = new int[ln][];
235 bkk = 0;
236
237 // break image in to 8x8 blocks
238 for (a = 0; a < (h * 8); a += 8)
239 {
240 // Get 8 lines of image
241 for (b = 0; b < (w * 8); b += 8)
242 {
243 // Get block for FDCT
244 for (c = 0; c < 8; c++)
245 {
246 for (d = 0; d < 8; d++)
247 {
248 // mask off 3 upper bytes of image data
249 blk[c][d] = (data[((a + c) * X) + b + d] & 0x000000ff) -
250 128;
251 }
252 }
253
254 //Do FDCT on Block
255 blocks[bkk++] = FDCT(blk);
256 }
257 }
258
259 // Write out header data
260 writeHeaders();
261
262 // Encode image scan
263 Huffman(blocks);
264
265 // Write out last bits of image scan
266 writeEndData();
267
268 // Write End of Image marker
269 writeEnd();
270 }
271
272 private void getPixels(int[] data)
273 {
274 PixelGrabber pg = new PixelGrabber(image.getSource(), 0, 0, X, Y, data,
275 0, X);
276
277 try
278 {
279 if (pg.grabPixels() != true)
280 {
281 try
282 {
283 throw new AWTException("Grabber returned false: " +
284 pg.status());
285 }
286 catch (Exception ex)
287 {
288 System.err.println("System Failed to get Pixels! - " + ex);
289 System.exit(0);
290 }
291
292 ;
293 }
294 }
295 catch (InterruptedException ent)
296 {
297 }
298
299 ;
300 }
301
302 private void Encode_AC_coefficients(int[] ZZ)
303 {
304 boolean done = false;
305 int size;
306 int code;
307 int dat;
308 K = 0;
309 R = 0;
310
311 while (!done)
312 {
313 K++;
314 dat = ZZ[K];
315
316 if (dat == 0)
317 {
318 if (K == 63)
319 {
320 // mark EOB
321 code = EHUFCO[0x00];
322 size = EHUFSI[0x00];
323 writeData(code, size);
324 done = true;
325
326 break;
327 }
328 else
329 {
330 R++;
331
332 continue;
333 }
334 }
335 else
336 {
337 while (true)
338 {
339 if (R > 15)
340 {
341 R -= 16;
342
343 // Mark RLZ in scan
344 code = EHUFCO[0xf0];
345 size = EHUFSI[0xf0];
346 writeData(code, size);
347
348 continue;
349 }
350
351 Encode_R(ZZ[K]);
352 R = 0;
353
354 if (K == 63)
355 {
356 done = true;
357 }
358
359 break;
360 }
361 }
362 }
363 }
364
365 private void Encode_R(int ZZ)
366 {
367 int dat;
368 int RS;
369 int size;
370 int code;
371 dat = ZZ;
372
373 if (ZZ < 0)
374 {
375 dat = -dat;
376 ZZ--;
377 }
378
379 // Get AC magnitude category
380 SSSS = MagCat(dat);
381 RS = (R << 4) + SSSS;
382
383 // append bits
384 code = EHUFCO[RS];
385 size = EHUFSI[RS];
386 writeData(code, size);
387
388 // Mask off upper bits of ZZ
389 ZZ &= ((1 << SSSS) - 1);
390
391 // append SSS low order bits of ZZ(K)
392 writeData(ZZ, SSSS);
393 }
394
395 private int[] FDCT(int[][] block)
396 {
397 int j1;
398 int i;
399 int j;
400 int[] blk = new int[64];
401 float[] b = new float[8];
402 float temp;
403 float[] b1 = new float[8];
404 float[][] d = new float[8][8];
405 float f0 = (float) 0.7071068;
406 float f1 = (float) 0.4903926;
407 float f2 = (float) 0.4619398;
408 float f3 = (float) 0.4157348;
409 float f4 = (float) 0.3535534;
410 float f5 = (float) 0.2777851;
411 float f6 = (float) 0.1913417;
412 float f7 = (float) 0.0975452;
413
414 float df7f1 = (float) -0.3928475;
415 float sf7f1 = (float) 0.5879378;
416 float df3f5 = (float) 0.1379497;
417 float sf3f5 = (float) 0.6935199;
418 float df6f2 = (float) -0.27059805;
419 float sf6f2 = (float) .6532815;
420
421 for (i = 0; i < 8; i++)
422 {
423 for (j = 0; j < 8; j++)
424 {
425 b[j] = block[i][j];
426 }
427
428 // Horizontal transform
429 for (j = 0; j < 4; j++)
430 {
431 j1 = 7 - j;
432 b1[j] = b[j] + b[j1];
433 b1[j1] = b[j] - b[j1];
434 }
435
436 b[0] = b1[0] + b1[3];
437 b[1] = b1[1] + b1[2];
438 b[2] = b1[1] - b1[2];
439 b[3] = b1[0] - b1[3];
440 b[4] = b1[4];
441 b[5] = (b1[6] - b1[5]) * f0;
442 b[6] = (b1[6] + b1[5]) * f0;
443 b[7] = b1[7];
444 d[i][0] = (b[0] + b[1]) * f4;
445 d[i][4] = (b[0] - b[1]) * f4;
446
447 temp = (b[3] + b[2]) * f6;
448 d[i][2] = temp - (b[3] * df6f2);
449 d[i][6] = temp - (b[2] * sf6f2);
450
451 b1[4] = b[4] + b[5];
452 b1[7] = b[7] + b[6];
453 b1[5] = b[4] - b[5];
454 b1[6] = b[7] - b[6];
455
456 temp = (b1[7] + b1[4]) * f7;
457 d[i][1] = temp - (b1[7] * df7f1);
458 d[i][7] = temp - (b1[4] * sf7f1);
459
460 temp = (b1[6] + b1[5]) * f3;
461 d[i][5] = temp - (b1[6] * df3f5);
462 d[i][3] = temp - (b1[5] * sf3f5);
463 }
464
465 // Vertical transform
466 for (i = 0; i < 8; i++)
467 {
468 for (j = 0; j < 4; j++)
469 {
470 j1 = 7 - j;
471 b1[j] = d[j][i] + d[j1][i];
472 b1[j1] = d[j][i] - d[j1][i];
473 }
474
475 b[0] = b1[0] + b1[3];
476 b[1] = b1[1] + b1[2];
477 b[2] = b1[1] - b1[2];
478 b[3] = b1[0] - b1[3];
479 b[4] = b1[4];
480 b[5] = (b1[6] - b1[5]) * f0;
481 b[6] = (b1[6] + b1[5]) * f0;
482 b[7] = b1[7];
483 d[0][i] = (b[0] + b[1]) * f4;
484 d[4][i] = (b[0] - b[1]) * f4;
485
486 temp = (b[3] + b[2]) * f6;
487 d[2][i] = temp - (b[3] * df6f2);
488 d[6][i] = temp - (b[2] * sf6f2);
489
490 b1[4] = b[4] + b[5];
491 b1[7] = b[7] + b[6];
492 b1[5] = b[4] - b[5];
493 b1[6] = b[7] - b[6];
494
495 temp = (b1[7] + b1[4]) * f7;
496 d[1][i] = temp - (b1[7] * df7f1);
497 d[7][i] = temp - (b1[4] * sf7f1);
498
499 temp = (b1[6] + b1[5]) * f3;
500 d[5][i] = temp - (b1[6] * df3f5);
501 d[3][i] = temp - (b1[5] * sf3f5);
502 }
503
504 for (i = 0; i < 8; i++)
505 {
506 for (j = 0; j < 8; j++)
507 {
508 // Quantize and ZigZag data block
509 blk[ZZ[i][j]] = (int) (d[i][j] / QT[i][j]);
510 }
511 }
512
513 return blk;
514 }
515
516 private void Generate_code_table()
517 {
518 // Generate Code table Flow Chart C.2
519 K = 0;
520 CODE = 0;
521 SI = HUFFSIZE[0];
522
523 while (true)
524 {
525 HUFFCODE[K++] = CODE++;
526
527 if (HUFFSIZE[K] == SI)
528 {
529 continue;
530 }
531
532 if (HUFFSIZE[K] == 0)
533 {
534 break;
535 }
536
537 while (true)
538 {
539 CODE <<= 1;
540 SI++;
541
542 if (HUFFSIZE[K] == SI)
543 {
544 break;
545 }
546 }
547 }
548 }
549
550 private void Generate_size_table()
551 {
552 // Generate HUFFSIZE table Flow Chart C.1
553 K = 0;
554 I = 1;
555 J = 1;
556
557 while (true)
558 {
559 if (J > BITS[I])
560 {
561 J = 1;
562 I++;
563
564 if (I > 16)
565 {
566 break;
567 }
568 }
569 else
570 {
571 HUFFSIZE[K++] = I;
572 J++;
573 }
574 }
575
576 HUFFSIZE[K] = 0;
577 LASTK = K;
578 }
579
580 private void Huffman(int[][] blocks)
581 {
582 int tmp;
583 int lp;
584 int DIFF;
585 int dat;
586 int PRED;
587 int bits;
588 int[] blk;
589 byte[] data = new byte[1];
590 int[][] HuffTbl =
591 {
592 {2, 0},
593 {3, 2},
594 {3, 3},
595 {3, 4},
596 {3, 5},
597 {3, 6},
598 {4, 14},
599 {5, 30},
600 {6, 62},
601 {7, 126},
602 {8, 254},
603 {9, 510}
604 };
605 long buffer;
606
607 // Encode data blocks
608 // Initialize predictor for new scan
609 PRED = 0;
610 BitCnt = 0;
611
612 for (lp = 0; lp < ln; lp++)
613 {
614 blk = blocks[lp];
615 buffer = 0;
616 bits = 0;
617
618 // Encode DC coefficient
619 tmp = DIFF = blk[0] - PRED;
620
621 if (tmp < 0)
622 {
623 tmp = -tmp;
624 DIFF--;
625 }
626
627 // find magnitude category data for DC edcoding
628 SSSS = MagCat(tmp);
629
630 // Total number of bits output
631 bits = HuffTbl[SSSS][0] + SSSS;
632
633 // Code word for this category shifted
634 //to accept magnitude data
635 buffer = HuffTbl[SSSS][1] << SSSS;
636
637 // append bits to code word
638 buffer += (DIFF & ((1 << SSSS) - 1));
639
640 // Write out data if greater then or equal to one byte
641 writeData(buffer, bits);
642
643 // Assign PRED for next DC DIFF
644 PRED = blk[0];
645
646 // Encode AC coefficients
647 Encode_AC_coefficients(blk);
648
649 // free up memory for next compression
650 blocks[lp] = null;
651 }
652 }
653
654 private int MagCat(int dat)
655 {
656 int ln;
657
658 for (;;)
659 {
660 if (dat == 0)
661 {
662 ln = 0;
663
664 break;
665 }
666
667 if (dat == 1)
668 {
669 ln = 1;
670
671 break;
672 }
673
674 if (dat <= 3)
675 {
676 ln = 2;
677
678 break;
679 }
680
681 if (dat <= 7)
682 {
683 ln = 3;
684
685 break;
686 }
687
688 if (dat <= 15)
689 {
690 ln = 4;
691
692 break;
693 }
694
695 if (dat <= 31)
696 {
697 ln = 5;
698
699 break;
700 }
701
702 if (dat <= 63)
703 {
704 ln = 6;
705
706 break;
707 }
708
709 if (dat <= 127)
710 {
711 ln = 7;
712
713 break;
714 }
715
716 if (dat <= 255)
717 {
718 ln = 8;
719
720 break;
721 }
722
723 if (dat <= 511)
724 {
725 ln = 9;
726
727 break;
728 }
729
730 if (dat <= 1023)
731 {
732 ln = 10;
733
734 break;
735 }
736
737 if (dat <= 2047)
738 {
739 ln = 11;
740
741 break;
742 }
743 }
744
745 return ln;
746 }
747
748 private void Order_codes()
749 {
750 // Order Codes Flow Chart C.3
751 K = 0;
752
753 while (true)
754 {
755 I = HUFFVAL[K];
756 EHUFCO[I] = HUFFCODE[K];
757 EHUFSI[I] = HUFFSIZE[K++];
758
759 if (K >= LASTK)
760 {
761 break;
762 }
763 }
764 }
765
766 private void writeData(long dat, int bits)
767 {
768 int tmp;
769 byte[] stuff = {0};
770 byte[] d = new byte[1];
771
772 if (bits > 0)
773 {
774 DATA <<= bits;
775 DATA += dat;
776 BitCnt += bits;
777 }
778 else
779 {
780 return;
781 }
782
783 // output bytes untill cnt is less then 8
784 while (BitCnt > 7)
785 {
786 BitCnt -= 8;
787 tmp = (int) (DATA >> BitCnt);
788 d[0] = (byte) tmp;
789
790 // mask off 8 msb of DATA
791 DATA &= ((1 << BitCnt) - 1);
792
793 // write out data
794 try
795 {
796 fos.write(d); // End of Image/File
797
798 if (d[0] == -1)
799 {
800 // if 0xFF data stuff 0x00 byte
801 fos.write(stuff);
802 }
803 }
804 catch (IOException ioe)
805 {
806 System.err.println("IOException: " + ioe);
807 }
808 }
809 }
810
811 private void writeEnd()
812 {
813 try
814 {
815 fos.write(EOI); // End of Image/File
816 fos.close();
817 }
818 catch (IOException ioe)
819 {
820 System.err.println("IOException: " + ioe);
821 }
822 }
823
824 private void writeEndData()
825 {
826 byte[] d = new byte[1];
827
828 // finish off scan data to byte boundry
829 if (BitCnt > 0)
830 {
831 DATA <<= (8 - BitCnt);
832 DATA += ((1 << (8 - BitCnt)) - 1);
833 d[0] = (byte) DATA;
834
835 try
836 {
837 fos.write(d);
838 }
839 catch (IOException ioe)
840 {
841 System.err.println("IOException: " + ioe);
842 }
843 }
844 }
845
846 private void writeHeaders()
847 {
848 try
849 {
850 fos.write(SOI); // Start of Image marker
851 fos.write(APP0); // Application header
852 fos.write(BE); // JFIF BE jpg compression
853 fos.write(QNT); // Quantization table
854 fos.write(SOF); // Start of Frame marker
855 fos.write(HuffDC); // DC Huffman Table
856 fos.write(HuffACHeader); // AC Huffman Table Header
857 fos.write(Bits); // AC Huffman Table
858 fos.write(Huffval); // AC Huffman Table
859 fos.write(SOS); // Start of Scan header
860 }
861 catch (IOException ioe)
862 {
863 System.err.println("IOException: " + ioe);
864 }
865 }
866 }
867 ///////////////////////////////////////////////////////////////////////////////
868 // END OF FILE.
869 ///////////////////////////////////////////////////////////////////////////////