001 package com.imagero.uio.io;
002
003 import java.awt.*;
004 import java.awt.event.ActionEvent;
005 import java.awt.event.ActionListener;
006 import java.io.ByteArrayInputStream;
007 import java.io.FilterOutputStream;
008 import java.io.IOException;
009 import java.io.OutputStream;
010
011 /**
012 * Date: 02.08.2007
013 *
014 * @author Andrey Kuznetsov
015 */
016 public class PackBitsOutputStream extends FilterOutputStream {
017
018 private RunBuffer runs;
019 int width;
020 int cnt;
021
022 public PackBitsOutputStream(OutputStream out, int width) {
023 super(out);
024 runs = new RunBuffer(out);
025 this.width = width;
026 }
027
028 public void write(int b) throws IOException {
029 put(b);
030 }
031
032 private void put(int b) throws IOException {
033 runs.put(b);
034 if (cnt++ == width) {
035 flush();
036 cnt = 0;
037 }
038 }
039
040 public void write(byte b[]) throws IOException {
041 for (int i = 0; i < b.length; i++) {
042 put(b[i] & 0xFF);
043 }
044 }
045
046 public void write(byte b[], int off, int len) throws IOException {
047 for (int i = 0; i < len; i++) {
048 put(b[off + i] & 0xFF);
049 }
050 }
051
052 public void flush() throws IOException {
053 runs.flush();
054 }
055
056 public void close() throws IOException {
057 runs.flush();
058 }
059
060 static class RunOutputStream extends FilterOutputStream {
061 int maxRun;
062 int rep = -2;
063
064 int count;
065
066 public RunOutputStream(OutputStream out, int maxRun) {
067 super(out);
068 this.maxRun = maxRun;
069 }
070
071 public void write(int b) throws IOException {
072 if (b != rep) {
073 throw new IOException("Rep");
074 }
075 if (count++ == maxRun) {
076 out.write(count - 1);
077 out.write(rep);
078 count = 0;
079 }
080 }
081
082 public void set(int rep) {
083 this.rep = rep;
084 count = 2;
085 }
086
087 public void flush() throws IOException {
088 if (count != 0) {
089 out.write(count - 1);
090 out.write(rep);
091 count = 0;
092 }
093 }
094 }
095
096 static class RunBuffer {
097 int rep;
098 int count;
099
100 OutputStream out;
101 RunOutputStream ros;
102 private LiteralBuffer lbuf;
103
104 public RunBuffer(OutputStream out) {
105 this.out = out;
106 ros = new RunOutputStream(out, 128);
107 this.lbuf = new LiteralBuffer(out);
108 }
109
110 public void put(int k) throws IOException {
111 if (count == 0) {
112 rep = k;
113 count = 1;
114 } else {
115 if (rep == k) {
116 if (count++ == 2) {
117 ros.set(rep);
118 lbuf.flush();
119 } else if (count > 2) {
120 ros.write(rep);
121 }
122 } else {
123 if (count > 2) {
124 ros.flush();
125 } else {
126 for (int i = 0; i < count; i++) {
127 lbuf.put(rep);
128 }
129 }
130 rep = k;
131 count = 1;
132 }
133 }
134 }
135
136 void flush() throws IOException {
137 if (count > 0) {
138 ros.flush();
139 for (int i = 0; i < count; i++) {
140 lbuf.put(rep);
141 }
142 count = 0;
143 }
144 lbuf.flush();
145 }
146
147 void write() throws IOException {
148 lbuf.flush();
149 out.write(-(count - 1));
150 out.write(rep);
151 count = 0;
152 }
153 }
154
155 static class LiteralBuffer {
156 byte[] buffer = new byte[129];
157 int count;
158
159 OutputStream out;
160
161 public LiteralBuffer(OutputStream out) {
162 this.out = out;
163 }
164
165 public void put(int k) throws IOException {
166 buffer[count++] = (byte) k;
167 if (count == 128) {
168 write();
169 }
170 }
171
172 void write() throws IOException {
173 out.write(count - 1);
174 out.write(buffer, 0, count);
175 count = 0;
176 }
177
178 void flush() throws IOException {
179 if (count > 0) {
180 write();
181 }
182 }
183 }
184
185 static class PeekInputStream extends ByteArrayInputStream {
186 int offset;
187
188 public PeekInputStream(byte buf[]) {
189 super(buf);
190 }
191
192 public PeekInputStream(byte buf[], int offset, int length) {
193 super(buf, offset, length);
194 this.offset = offset;
195 }
196
197 public int peek(int offset) {
198 return buf[pos + offset];
199 }
200
201 public int getPosition() {
202 return pos - offset;
203 }
204
205 public void inc() {
206 pos++;
207 }
208 }
209
210 public static class PackBitsApache extends FilterOutputStream {
211
212 int bytesPerRow;
213 int inMax;
214 int inMaxMinus1;
215
216 PeekInputStream input;
217 byte[] tmp = new byte[128];
218
219 int wpos = 0;
220 byte[] row;
221
222 public PackBitsApache(OutputStream out, int bytesPerRow) {
223 super(out);
224 this.bytesPerRow = bytesPerRow;
225 inMax = bytesPerRow - 1;
226 inMaxMinus1 = inMax - 1;
227 this.row = new byte[bytesPerRow];
228 }
229
230 public void write(int b) throws IOException {
231 row[wpos++] = (byte) b;
232 if (wpos == bytesPerRow) {
233 wpos = 0;
234 input = new PeekInputStream(row);
235 packBits();
236 }
237 }
238
239 public void write(byte b[]) throws IOException {
240 write(b, 0, b.length);
241 }
242
243 public void write(byte b[], int off, int len) throws IOException {
244 for (int i = 0; i < len; i++) {
245 write(b[off + i] & 0xFF);
246 }
247 }
248
249 public void flush() throws IOException {
250 if (wpos > 0) {
251 input = new PeekInputStream(row, 0, wpos);
252 packBits();
253 }
254 }
255
256 private void packBits() throws IOException {
257 while (input.getPosition() <= inMax) {
258 doRunLoop();
259 doLiterLoop();
260 }
261 }
262
263 private void doLiterLoop() throws IOException {
264 int run = 0;
265 while (run < 128 &&
266 ((input.getPosition() < inMax) && (input.peek(0) != input.peek(1))
267 || ((input.getPosition() < inMaxMinus1) && (input.peek(0) != input.peek(2))))) {
268 tmp[run++] = (byte) input.read();
269 }
270
271 if (input.getPosition() == inMax && (run > 0 && run < 128)) {
272 tmp[run++] = (byte) input.read();
273 }
274
275 if (run > 0) {
276 out.write(run - 1);
277 out.write(tmp, 0, run);
278 } else if (input.getPosition() == inMax) {
279 out.write(0);
280 out.write(input.read() & 0xFF);
281 }
282 }
283
284 private void doRunLoop() throws IOException {
285 int run = 1;
286 byte replicate = (byte) input.peek(0);
287 while (run < 127 && input.getPosition() < inMax && input.peek(0) == input.peek(1)) {
288 run++;
289 input.inc();
290 }
291 if (run > 1) {
292 input.inc();
293 out.write(-(run - 1));
294 out.write(replicate);
295 }
296 }
297 }
298
299 static class PBApache {
300 static int compressPackBits(byte[] data, int numRows, int bytesPerRow, byte[] compData) {
301 int inOffset = 0;
302 int outOffset = 0;
303 byte[] tmp = new byte[128];
304 for (int i = 0; i < numRows; i++) {
305 outOffset = packBits(data, inOffset, bytesPerRow, compData, outOffset, tmp);
306 inOffset += bytesPerRow;
307 }
308 return outOffset;
309 }
310
311 private static int packBits(byte[] input, int inOffset, int inCount, byte[] output, int outOffset, byte[] tmp) {
312 int inMax = inOffset + inCount - 1;
313 int inMaxMinus1 = inMax - 1;
314 while (inOffset <= inMax) {
315 int run = 1;
316 byte replicate = input[inOffset];
317 while (run < 127 && inOffset < inMax && input[inOffset] == input[inOffset + 1]) {
318 run++;
319 inOffset++;
320 }
321 if (run > 1) {
322 inOffset++;
323 output[outOffset++] = (byte) (-(run - 1));
324 output[outOffset++] = replicate;
325 }
326 run = 0;
327 while (run < 128 && ((inOffset < inMax && input[inOffset] != input[inOffset + 1]) || (inOffset < inMaxMinus1 && input[inOffset] != input[inOffset + 2]))) {
328 tmp[run++] = input[inOffset++];
329 }
330 if (inOffset == inMax && (run > 0 && run < 128)) {
331 tmp[run++] = input[inOffset++];
332 }
333 if (run > 0) {
334 output[outOffset++] = (byte) (run - 1);
335 for (int i = 0; i < run; i++) {
336 output[outOffset++] = tmp[i];
337 }
338 } else if (inOffset == inMax) {
339 output[outOffset++] = (byte) 0;
340 output[outOffset++] = input[inOffset++];
341 }
342 }
343 return outOffset;
344 }
345 }
346
347 // public static void main(String[] args) throws IOException {
348 // String s = "C:\\Support\\AndySwanson\\work\\IMG_0703_source_RC1.jpg";
349 //// String s = "C:\\Support\\AndySwanson\\work\\IMG_0703_source_RC1_LZW.tif";
350 //// String s = "C:\\Support\\AndySwanson\\work\\IMG_0703_source_RC1_RLE.tif";
351 //// String d = "C:\\Support\\AndySwanson\\work\\IMG_0703_source_RC1.tif";
352 //
353 //
354 // ImageReader reader = ReaderFactory.createReader(s);
355 // int[] data = (int[]) reader.getAsArray(0, true);
356 // byte[] bdata = new byte[data.length * 3];
357 //
358 // IntArrayInputStream iais = new IntArrayInputStream(data);
359 // SkipBytesInputStream sbis = new SkipBytesInputStream(iais, 2 + 4 + 8, 4);
360 //
361 // ByteArrayOutputStream2 out2 = new ByteArrayOutputStream2(bdata);
362 // IOutils.copy(sbis, out2);
363 //
364 // int w = reader.getWidth(0);
365 // int h = reader.getHeight(0);
366 // int w3 = w * 3;
367 //
368 // ByteArrayOutputStream bout = new ByteArrayOutputStream();
369 // PackBitsApache packBitsApache = new PackBitsApache(bout, w3);
370 // packBitsApache.write(bdata);
371 // packBitsApache.close();
372 // byte[] res = bout.toByteArray();
373 //
374 // byte[] compData = new byte[res.length];
375 // int length = PBApache.compressPackBits(bdata, h, w3, compData);
376 //
377 // System.out.println(compData.length);
378 // System.out.println(res.length);
379 //
380 // for (int i = 0; i < length; i++) {
381 // if (compData[i] != res[i]) {
382 // System.out.println("aaaaaaaaaaa");
383 // System.out.println(i);
384 // System.out.println(compData[i]);
385 // System.out.println(res[i]);
386 // return;
387 // }
388 // }
389 // System.out.println("identisch");
390 //
391 //
392 // ByteArrayOutputStream bout2 = new ByteArrayOutputStream();
393 //// ParserOutputStream pos = new ParserOutputStream();
394 //// MultiplexOutputStream mos = new MultiplexOutputStream(bout2, pos);
395 //// mos.setSearchPosition(1930);
396 //// mos.addActionListener(new ActionListener() {
397 //// public void actionPerformed(ActionEvent e) {
398 //// System.out.println("position");
399 //// }
400 //// });
401 //
402 //// PackBitsOutputStream pbos = new PackBitsOutputStream(mos, w3);
403 // Control pbos = new Control(bout2, w3);
404 // pbos.write(bdata);
405 // byte[] pbosData = bout2.toByteArray();
406 // System.out.println(pbosData.length + " " + length);
407 //
408 // for (int i = 0; i < length; i++) {
409 // int k1 = pbosData[i] & 0xFF;
410 // int m1 = res[i] & 0xFF;
411 // if (k1 == m1) {
412 //// System.out.println(Integer.toHexString(k) + " " + Integer.toHexString(m));
413 // } else {
414 //// int k = pbosData[i - 2] & 0xFF;
415 //// int k0 = pbosData[i - 1] & 0xFF;
416 //// int k2 = pbosData[i + 1] & 0xFF;
417 //// int k3 = pbosData[i + 2] & 0xFF;
418 //// int k4 = pbosData[i + 3] & 0xFF;
419 //// int m = res[i - 2] & 0xFF;
420 //// int m0 = res[i - 1] & 0xFF;
421 //// int m2 = res[i + 1] & 0xFF;
422 //// int m3 = res[i + 2] & 0xFF;
423 //// int m4 = res[i + 3] & 0xFF;
424 //// System.out.println((i - 2) + " " + Integer.toHexString(k) + " " + Integer.toHexString(m) + " *");
425 //// System.out.println((i - 1) + " " + Integer.toHexString(k0) + " " + Integer.toHexString(m0) + " *");
426 // System.out.println(i + " " + Integer.toHexString(k1) + " " + Integer.toHexString(m1) + " *");
427 //// System.out.println((i + 1) + " " + Integer.toHexString(k2) + " " + Integer.toHexString(m2) + " *");
428 //// System.out.println((i + 2) + " " + Integer.toHexString(k3) + " " + Integer.toHexString(m3) + " *");
429 //// System.out.println((i + 3) + " " + Integer.toHexString(k4) + " " + Integer.toHexString(m4) + " *");
430 //// break;
431 //// System.out.println("*****************");
432 // }
433 // }
434 // }
435
436 static class MultiplexOutputStream extends OutputStream {
437
438 OutputStream out1, out2;
439 long p = 0;
440
441 long searchPosition = -1;
442
443 ActionListener listener;
444
445 public MultiplexOutputStream(OutputStream out1, OutputStream out2) {
446 this.out1 = out1;
447 this.out2 = out2;
448 }
449
450 public void write(int b) throws IOException {
451 inc();
452 out1.write(b);
453 out2.write(b);
454 }
455
456 public void addActionListener(ActionListener listener) {
457 this.listener = AWTEventMulticaster.add(this.listener, listener);
458 }
459
460 public void removeActionListener(ActionListener listener) {
461 this.listener = AWTEventMulticaster.remove(this.listener, listener);
462 }
463
464 public long getSearchPosition() {
465 return searchPosition;
466 }
467
468 public void setSearchPosition(long searchPosition) {
469 this.searchPosition = searchPosition;
470 }
471
472 private void inc() {
473 if (p++ == searchPosition) {
474 fireActionEvent();
475 }
476 }
477
478 private void fireActionEvent() {
479 if (listener != null) {
480 listener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "Position"));
481 }
482 }
483
484 public void write(byte b[]) throws IOException {
485 inc(b.length);
486 out1.write(b);
487 out2.write(b);
488 }
489
490 private void inc(int length) {
491 p += length;
492 if (p - length <= searchPosition && p >= searchPosition) {
493 fireActionEvent();
494 }
495 }
496
497 public void write(byte b[], int off, int len) throws IOException {
498 inc(len);
499 out1.write(b, off, len);
500 out2.write(b, off, len);
501 }
502
503 public void flush() throws IOException {
504 out1.flush();
505 out2.flush();
506 }
507
508 public void close() throws IOException {
509 out1.close();
510 out2.close();
511 }
512 }
513
514 // static class ParserOutputStream extends OutputStream {
515 //
516 // StreamParser sp;
517 // long p;
518 //
519 // public ParserOutputStream() {
520 // sp = new StreamParser() {
521 // protected StreamParser.CharHandler createParameterHandler() {
522 // return new ParameterHandler(this, new char[0], new char[0], new char[0]) {
523 // public boolean nextChar(char c, long offset) {
524 // ICharSequence[] prms = new ICharSequence[params.getCount()];
525 // Entry entry = putParam(prms, offset);
526 // parser.postParserEvent(this, entry);
527 // setHandler(getNext());
528 // return false;
529 // }
530 // };
531 // }
532 // };
533 // byte[] bstr = {0x53, 0x61, 0x0, 0x6f, 0x11, 0x7a};
534 // sp.register(new VKey(new String(bstr), VKey.KEY_TYPE_COMMENT));
535 //
536 // sp.addParserListener(new ParserListener() {
537 // public boolean gotToken(ParserEvent e) {
538 // Entry entry = e.getEntry();
539 // long offset = entry.getOffset();
540 // Object value = entry.getValue();
541 // System.out.println(offset);
542 // System.out.println(value);
543 // return true;
544 // }
545 // });
546 // }
547 //
548 // public void write(int b) throws IOException {
549 // sp.nextChar((char) b, p++);
550 // }
551 //
552 // public void write(byte b[]) throws IOException {
553 // write(b, 0, b.length);
554 // }
555 //
556 // public void write(byte b[], int off, int len) throws IOException {
557 // for (int i = 0; i < len; i++) {
558 // sp.nextChar((char) b[off + i], p++);
559 // }
560 // }
561 // }
562
563 static interface Writer {
564 boolean next(byte b) throws IOException;
565
566 byte get();
567
568 void flush() throws IOException;
569
570 Writer nextWriter();
571 }
572
573 static class Control extends FilterOutputStream {
574 Run run;
575 Liter liter;
576
577 Writer current;
578 Writer last;
579
580 int count;
581 int max;
582
583 public Control(OutputStream out, int max) {
584 super(out);
585 this.max = max;
586 run = new Run(out);
587 liter = new Liter(out);
588 run.nextWriter = liter;
589 liter.nextWriter = run;
590 current = run;
591 }
592
593 public void write(int b) throws IOException {
594 count++;
595 if (!current.next((byte) b)) {
596 last = current;
597 current = current.nextWriter();
598 byte n;
599 while ((n = last.get()) != -1) {
600 current.next(n);
601 }
602 }
603 if (count == max) {
604 count = 0;
605 current.flush();
606 }
607 }
608
609 public void write(byte b[], int off, int len) throws IOException {
610 for (int i = 0; i < b.length; i++) {
611 write(b[i] & 0xFF);
612 }
613 }
614 }
615
616 static class Run implements Writer {
617 int run;
618 int count;
619 int w;
620
621 int rep;
622
623 byte last;
624
625 Writer nextWriter;
626
627 OutputStream out;
628
629 public Run(OutputStream out) {
630 this.out = out;
631 }
632
633 public Writer nextWriter() {
634 return nextWriter;
635 }
636
637 void init(byte rep, int count, int run) {
638 this.rep = rep;
639 this.run = run;
640 this.count = count;
641 }
642
643 public byte get() {
644 byte tmp = last;
645 last = -1;
646 return tmp;
647 }
648
649 public boolean next(byte b) throws IOException {
650 last = b;
651 if (run == 0) {
652 rep = b & 0xFF;
653 run++;
654 return true;
655 }
656 if (b == rep) {
657 run++;
658 count++;
659 if (run == 128) {
660 write();
661 }
662 return true;
663 } else {
664 if (run > 1) {
665 write();
666 }
667 return false;
668 }
669 }
670
671 private void write() throws IOException {
672 out.write(-(run - 1));
673 out.write(rep);
674 run = 0;
675 }
676
677 public void flush() throws IOException {
678 if (run > 0) {
679 write();
680 }
681 }
682 }
683
684 static class Liter implements Writer {
685 int b0 = -1;
686 int b1 = -1;
687 int b2 = -1;
688 int run;
689 int count;
690 int max;
691
692 int pos;
693 byte[] buffer = new byte[128];
694
695 Writer nextWriter;
696
697 OutputStream out;
698
699 public Liter(OutputStream out) {
700 this.out = out;
701 }
702
703 public Writer nextWriter() {
704 return nextWriter;
705 }
706
707 public boolean next(byte a) throws IOException {
708 b2 = a & 0xFF;
709 if (run > 0) {
710 if (b0 == b1 && b0 == b2) {
711 write();
712 return false;
713 }
714 }
715 shift();
716 if (run == 128) {
717 write();
718 }
719 return true;
720 }
721
722 void shift() throws IOException {
723 if (b0 != -1) {
724 add(b0);
725 }
726 b0 = b1;
727 b1 = b2;
728 b2 = -1;
729 }
730
731 public byte get() {
732 byte b = (byte) b0;
733 b0 = b1;
734 b1 = -1;
735 return b;
736 }
737
738 private void add(int b) {
739 buffer[run++] = (byte) b;
740 count++;
741 }
742
743 public void flush() throws IOException {
744 if (run > 0 && run < 128) {
745 shift();
746 write();
747 } else if (run == 0) {
748 shift();
749 out.write(0);
750 out.write(buffer[0] & 0xFF);
751 }
752 }
753
754 private void write() throws IOException {
755 if (run > 0) {
756 out.write(run - 1);
757 out.write(buffer, 0, run);
758 }
759 run = 0;
760 }
761 }
762 }