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    }