Browse code

add updated pngdecoder

devnewton authored on 24/07/2014 at 12:36:20
Showing 5 changed files
... ...
@@ -117,7 +117,7 @@ THE SOFTWARE. -->
117 117
             <artifactId>smjpegdecoder</artifactId>
118 118
         </dependency>
119 119
         <dependency>
120
-            <groupId>org.l33tlabs.twl</groupId>
120
+            <groupId>im.bci</groupId>
121 121
             <artifactId>pngdecoder</artifactId>
122 122
         </dependency>
123 123
         <dependency>
124 124
new file mode 100644
... ...
@@ -0,0 +1,125 @@
0
+<?xml version="1.0" encoding="UTF-8"?>
1
+<!-- The MIT License (MIT)
2
+
3
+Copyright (c) 2014 devnewton <devnewton@bci.im>
4
+
5
+Permission is hereby granted, free of charge, to any person obtaining a copy
6
+of this software and associated documentation files (the "Software"), to deal
7
+in the Software without restriction, including without limitation the rights
8
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+copies of the Software, and to permit persons to whom the Software is
10
+furnished to do so, subject to the following conditions:
11
+
12
+The above copyright notice and this permission notice shall be included in
13
+all copies or substantial portions of the Software.
14
+
15
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+THE SOFTWARE. -->
22
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
23
+    <modelVersion>4.0.0</modelVersion>
24
+    <parent>
25
+        <groupId>im.bci</groupId>
26
+        <artifactId>jnuit</artifactId>
27
+        <version>0.12-SNAPSHOT</version>
28
+        <relativePath>../pom.xml</relativePath>
29
+    </parent>
30
+    <groupId>im.bci</groupId>
31
+    <artifactId>pngdecoder</artifactId>
32
+    <packaging>jar</packaging>
33
+    <name>pngdecoder</name>
34
+    <description>Java PNG decoder</description>
35
+    <url>http://git.bci.im/jnuit</url>
36
+    <licenses>
37
+        <license>
38
+            <name>BSD</name>
39
+            <url>http://git.bci.im/jnuit/blob/master/pngdecoder/LICENSE</url>
40
+            <distribution>repo</distribution>
41
+        </license>
42
+    </licenses>
43
+    <developers>
44
+        <developer>
45
+            <name>Matthias Mann</name>
46
+            <url>http://www.matthiasmann.de/</url>
47
+        </developer>
48
+    </developers>
49
+    <properties>
50
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
51
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
52
+    </properties>
53
+    <build>
54
+        <resources>
55
+            <resource>
56
+                <directory>src/main/resources</directory>
57
+                <filtering>false</filtering>
58
+            </resource>
59
+        </resources>
60
+        <plugins>
61
+            <plugin>
62
+                <artifactId>maven-compiler-plugin</artifactId>
63
+            </plugin> 
64
+            <plugin>
65
+                <groupId>org.apache.maven.plugins</groupId>
66
+                <artifactId>maven-source-plugin</artifactId>
67
+            </plugin>
68
+            <plugin>
69
+                <groupId>org.apache.maven.plugins</groupId>
70
+                <artifactId>maven-javadoc-plugin</artifactId>
71
+                <version>2.9.1</version>
72
+                <executions>
73
+                    <execution>
74
+                        <id>attach-javadocs</id>
75
+                        <goals>
76
+                            <goal>jar</goal>
77
+                        </goals>
78
+                    </execution>
79
+                </executions>
80
+            </plugin>
81
+            <plugin>
82
+                <groupId>org.apache.maven.plugins</groupId>
83
+                <artifactId>maven-release-plugin</artifactId>
84
+                <version>2.4.2</version>
85
+                <configuration>
86
+                    <goals>deploy</goals>
87
+                    <pushChanges>false</pushChanges>
88
+                    <localCheckout>true</localCheckout>
89
+                </configuration>
90
+            </plugin>
91
+        </plugins>
92
+    </build>
93
+    <profiles>
94
+        <profile>
95
+            <id>release-sign-artifacts</id>
96
+            <activation>
97
+                <property>
98
+                    <name>performRelease</name>
99
+                    <value>true</value>
100
+                </property>
101
+            </activation>
102
+            <build>
103
+                <plugins>
104
+                    <plugin>
105
+                        <groupId>org.apache.maven.plugins</groupId>
106
+                        <artifactId>maven-gpg-plugin</artifactId>
107
+                        <version>1.4</version>
108
+                        <executions>
109
+                            <execution>
110
+                                <id>sign-artifacts</id>
111
+                                <phase>verify</phase>
112
+                                <goals>
113
+                                    <goal>sign</goal>
114
+                                </goals>
115
+                            </execution>
116
+                        </executions>
117
+                    </plugin>
118
+                </plugins>
119
+            </build>
120
+        </profile>
121
+    </profiles>
122
+</project>
123
+
124
+
0 125
new file mode 100644
... ...
@@ -0,0 +1,849 @@
0
+/*
1
+ * Copyright (c) 2008-2010, Matthias Mann
2
+ *
3
+ * All rights reserved.
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ *     * Redistributions of source code must retain the above copyright notice,
9
+ *       this list of conditions and the following disclaimer.
10
+ *     * Redistributions in binary form must reproduce the above copyright
11
+ *       notice, this list of conditions and the following disclaimer in the
12
+ *       documentation and/or other materials provided with the distribution.
13
+ *     * Neither the name of Matthias Mann nor the names of its contributors may
14
+ *       be used to endorse or promote products derived from this software
15
+ *       without specific prior written permission.
16
+ *     * Redistributions in source or binary form must keep the original package
17
+ *       and class name.
18
+ *
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
+ */
31
+package de.matthiasmann.twl.utils;
32
+
33
+import java.io.EOFException;
34
+import java.io.IOException;
35
+import java.io.InputStream;
36
+import java.nio.ByteBuffer;
37
+import java.util.Arrays;
38
+import java.util.zip.CRC32;
39
+import java.util.zip.DataFormatException;
40
+import java.util.zip.Inflater;
41
+
42
+/**
43
+ * A PNGDecoder. The slick PNG decoder is based on this class :)
44
+ * 
45
+ * @author Matthias Mann
46
+ */
47
+public class PNGDecoder {
48
+
49
+    public enum Format {
50
+        ALPHA(1, true),
51
+        LUMINANCE(1, false),
52
+        LUMINANCE_ALPHA(2, true),
53
+        RGB(3, false),
54
+        RGBA(4, true),
55
+        BGRA(4, true),
56
+        ABGR(4, true);
57
+
58
+        final int numComponents;
59
+        final boolean hasAlpha;
60
+
61
+        private Format(int numComponents, boolean hasAlpha) {
62
+            this.numComponents = numComponents;
63
+            this.hasAlpha = hasAlpha;
64
+        }
65
+
66
+        public int getNumComponents() {
67
+            return numComponents;
68
+        }
69
+
70
+        public boolean isHasAlpha() {
71
+            return hasAlpha;
72
+        }
73
+    }
74
+
75
+    private static final byte[] SIGNATURE = {(byte)137, 80, 78, 71, 13, 10, 26, 10};
76
+
77
+    private static final int IHDR = 0x49484452;
78
+    private static final int PLTE = 0x504C5445;
79
+    private static final int tRNS = 0x74524E53;
80
+    private static final int IDAT = 0x49444154;
81
+    private static final int IEND = 0x49454E44;
82
+    
83
+    private static final byte COLOR_GREYSCALE = 0;
84
+    private static final byte COLOR_TRUECOLOR = 2;
85
+    private static final byte COLOR_INDEXED = 3;
86
+    private static final byte COLOR_GREYALPHA = 4;
87
+    private static final byte COLOR_TRUEALPHA = 6;  
88
+    
89
+    private final InputStream input;
90
+    private final CRC32 crc;
91
+    private final byte[] buffer;
92
+    
93
+    private int chunkLength;
94
+    private int chunkType;
95
+    private int chunkRemaining;
96
+    
97
+    private int width;
98
+    private int height;
99
+    private int bitdepth;
100
+    private int colorType;
101
+    private int bytesPerPixel;
102
+    private byte[] palette;
103
+    private byte[] paletteA;
104
+    private byte[] transPixel;
105
+    
106
+    public PNGDecoder(InputStream input) throws IOException {
107
+        this.input = input;
108
+        this.crc = new CRC32();
109
+        this.buffer = new byte[4096];
110
+        
111
+        readFully(buffer, 0, SIGNATURE.length);
112
+        if(!checkSignature(buffer)) {
113
+            throw new IOException("Not a valid PNG file");
114
+        }
115
+        
116
+        openChunk(IHDR);
117
+        readIHDR();
118
+        closeChunk();
119
+        
120
+        searchIDAT: for(;;) {
121
+            openChunk();
122
+            switch (chunkType) {
123
+            case IDAT:
124
+                break searchIDAT;
125
+            case PLTE:
126
+                readPLTE();
127
+                break;
128
+            case tRNS:
129
+                readtRNS();
130
+                break;
131
+            }
132
+            closeChunk();
133
+        }
134
+
135
+        if(colorType == COLOR_INDEXED && palette == null) {
136
+            throw new IOException("Missing PLTE chunk");
137
+        }
138
+    }
139
+
140
+    public int getHeight() {
141
+        return height;
142
+    }
143
+
144
+    public int getWidth() {
145
+        return width;
146
+    }
147
+    
148
+    /**
149
+     * Checks if the image has a real alpha channel.
150
+     * This method does not check for the presence of a tRNS chunk.
151
+     *
152
+     * @return true if the image has an alpha channel
153
+     * @see #hasAlpha()
154
+     */
155
+    public boolean hasAlphaChannel() {
156
+        return colorType == COLOR_TRUEALPHA || colorType == COLOR_GREYALPHA;
157
+    }
158
+
159
+    /**
160
+     * Checks if the image has transparency information either from
161
+     * an alpha channel or from a tRNS chunk.
162
+     * 
163
+     * @return true if the image has transparency
164
+     * @see #hasAlphaChannel()
165
+     * @see #overwriteTRNS(byte, byte, byte)
166
+     */
167
+    public boolean hasAlpha() {
168
+        return hasAlphaChannel() ||
169
+                paletteA != null || transPixel != null;
170
+    }
171
+    
172
+    public boolean isRGB() {
173
+        return colorType == COLOR_TRUEALPHA ||
174
+                colorType == COLOR_TRUECOLOR ||
175
+                colorType == COLOR_INDEXED;
176
+    }
177
+
178
+    /**
179
+     * Overwrites the tRNS chunk entry to make a selected color transparent.
180
+     * <p>This can only be invoked when the image has no alpha channel.</p>
181
+     * <p>Calling this method causes {@link #hasAlpha()} to return true.</p>
182
+     *
183
+     * @param r the red component of the color to make transparent
184
+     * @param g the green component of the color to make transparent
185
+     * @param b the blue component of the color to make transparent
186
+     * @throws UnsupportedOperationException if the tRNS chunk data can't be set
187
+     * @see #hasAlphaChannel() 
188
+     */
189
+    public void overwriteTRNS(byte r, byte g, byte b) {
190
+        if(hasAlphaChannel()) {
191
+            throw new UnsupportedOperationException("image has an alpha channel");
192
+        }
193
+        byte[] pal = this.palette;
194
+        if(pal == null) {
195
+            transPixel = new byte[] { 0, r, 0, g, 0, b };
196
+        } else {
197
+            paletteA = new byte[pal.length/3];
198
+            for(int i=0,j=0 ; i<pal.length ; i+=3,j++) {
199
+                if(pal[i] != r || pal[i+1] != g || pal[i+2] != b) {
200
+                    paletteA[j] = (byte)0xFF;
201
+                }
202
+            }
203
+        }
204
+    }
205
+
206
+    /**
207
+     * Computes the implemented format conversion for the desired format.
208
+     *
209
+     * @param fmt the desired format
210
+     * @return format which best matches the desired format
211
+     * @throws UnsupportedOperationException if this PNG file can't be decoded
212
+     */
213
+    public Format decideTextureFormat(Format fmt) {
214
+        switch (colorType) {
215
+        case COLOR_TRUECOLOR:
216
+            switch (fmt) {
217
+            case ABGR:
218
+            case RGBA:
219
+            case BGRA:
220
+            case RGB: return fmt;
221
+            default: return Format.RGB;
222
+            }
223
+        case COLOR_TRUEALPHA:
224
+            switch (fmt) {
225
+            case ABGR:
226
+            case RGBA:
227
+            case BGRA:
228
+            case RGB: return fmt;
229
+            default: return Format.RGBA;
230
+            }
231
+        case COLOR_GREYSCALE:
232
+            switch (fmt) {
233
+            case LUMINANCE:
234
+            case ALPHA: return fmt;
235
+            default: return Format.LUMINANCE;
236
+            }
237
+        case COLOR_GREYALPHA:
238
+            return Format.LUMINANCE_ALPHA;
239
+        case COLOR_INDEXED:
240
+            switch (fmt) {
241
+            case ABGR:
242
+            case RGBA:
243
+            case BGRA: return fmt;
244
+            default: return Format.RGBA;
245
+            }
246
+        default:
247
+            throw new UnsupportedOperationException("Not yet implemented");
248
+        }
249
+    }
250
+
251
+    /**
252
+     * Decodes the image into the specified buffer. The first line is placed at
253
+     * the current position. After decode the buffer position is at the end of
254
+     * the last line.
255
+     *
256
+     * @param buffer the buffer
257
+     * @param stride the stride in bytes from start of a line to start of the next line, can be negative.
258
+     * @param fmt the target format into which the image should be decoded.
259
+     * @throws IOException if a read or data error occurred
260
+     * @throws IllegalArgumentException if the start position of a line falls outside the buffer
261
+     * @throws UnsupportedOperationException if the image can't be decoded into the desired format
262
+     */
263
+    public void decode(ByteBuffer buffer, int stride, Format fmt) throws IOException {
264
+        final int offset = buffer.position();
265
+        final int lineSize = ((width * bitdepth + 7) / 8) * bytesPerPixel;
266
+        byte[] curLine = new byte[lineSize+1];
267
+        byte[] prevLine = new byte[lineSize+1];
268
+        byte[] palLine = (bitdepth < 8) ? new byte[width+1] : null;
269
+        
270
+        final Inflater inflater = new Inflater();
271
+        try {
272
+            for(int y=0 ; y<height ; y++) {
273
+                readChunkUnzip(inflater, curLine, 0, curLine.length);
274
+                unfilter(curLine, prevLine);
275
+
276
+                buffer.position(offset + y*stride);
277
+
278
+                switch (colorType) {
279
+                case COLOR_TRUECOLOR:
280
+                    switch (fmt) {
281
+                    case ABGR: copyRGBtoABGR(buffer, curLine); break;
282
+                    case RGBA: copyRGBtoRGBA(buffer, curLine); break;
283
+                    case BGRA: copyRGBtoBGRA(buffer, curLine); break;
284
+                    case RGB: copy(buffer, curLine); break;
285
+                    default: throw new UnsupportedOperationException("Unsupported format for this image");
286
+                    }
287
+                    break;
288
+                case COLOR_TRUEALPHA:
289
+                    switch (fmt) {
290
+                    case ABGR: copyRGBAtoABGR(buffer, curLine); break;
291
+                    case RGBA: copy(buffer, curLine); break;
292
+                    case BGRA: copyRGBAtoBGRA(buffer, curLine); break;
293
+                    case RGB: copyRGBAtoRGB(buffer, curLine); break;
294
+                    default: throw new UnsupportedOperationException("Unsupported format for this image");
295
+                    }
296
+                    break;
297
+                case COLOR_GREYSCALE:
298
+                    switch (fmt) {
299
+                    case LUMINANCE:
300
+                    case ALPHA: copy(buffer, curLine); break;
301
+                    default: throw new UnsupportedOperationException("Unsupported format for this image");
302
+                    }
303
+                    break;
304
+                case COLOR_GREYALPHA:
305
+                    switch (fmt) {
306
+                    case LUMINANCE_ALPHA: copy(buffer, curLine); break;
307
+                    default: throw new UnsupportedOperationException("Unsupported format for this image");
308
+                    }
309
+                    break;
310
+                case COLOR_INDEXED:
311
+                    switch(bitdepth) {
312
+                        case 8: palLine = curLine; break;
313
+                        case 4: expand4(curLine, palLine); break;
314
+                        case 2: expand2(curLine, palLine); break;
315
+                        case 1: expand1(curLine, palLine); break;
316
+                        default: throw new UnsupportedOperationException("Unsupported bitdepth for this image");
317
+                    }
318
+                    switch (fmt) {
319
+                    case ABGR: copyPALtoABGR(buffer, palLine); break;
320
+                    case RGBA: copyPALtoRGBA(buffer, palLine); break;
321
+                    case BGRA: copyPALtoBGRA(buffer, palLine); break;
322
+                    case RGB: copyPALtoRGB(buffer, palLine); break;
323
+                    default: throw new UnsupportedOperationException("Unsupported format for this image");
324
+                    }
325
+                    break;
326
+                default:
327
+                    throw new UnsupportedOperationException("Not yet implemented");
328
+                }
329
+
330
+                byte[] tmp = curLine;
331
+                curLine = prevLine;
332
+                prevLine = tmp;
333
+            }
334
+        } finally {
335
+            inflater.end();
336
+        }
337
+    }
338
+
339
+    /**
340
+     * Decodes the image into the specified buffer. The last line is placed at
341
+     * the current position. After decode the buffer position is at the end of
342
+     * the first line.
343
+     *
344
+     * @param buffer the buffer
345
+     * @param stride the stride in bytes from start of a line to start of the next line, must be positive.
346
+     * @param fmt the target format into which the image should be decoded.
347
+     * @throws IOException if a read or data error occurred
348
+     * @throws IllegalArgumentException if the start position of a line falls outside the buffer
349
+     * @throws UnsupportedOperationException if the image can't be decoded into the desired format
350
+     */
351
+    public void decodeFlipped(ByteBuffer buffer, int stride, Format fmt) throws IOException {
352
+        if(stride <= 0) {
353
+            throw new IllegalArgumentException("stride");
354
+        }
355
+        int pos = buffer.position();
356
+        int posDelta = (height-1) * stride;
357
+        buffer.position(pos + posDelta);
358
+        decode(buffer, -stride, fmt);
359
+        buffer.position(buffer.position() + posDelta);
360
+    }
361
+    
362
+    private void copy(ByteBuffer buffer, byte[] curLine) {
363
+        buffer.put(curLine, 1, curLine.length-1);
364
+    }
365
+
366
+    private void copyRGBtoABGR(ByteBuffer buffer, byte[] curLine) {
367
+        if(transPixel != null) {
368
+            byte tr = transPixel[1];
369
+            byte tg = transPixel[3];
370
+            byte tb = transPixel[5];
371
+            for(int i=1,n=curLine.length ; i<n ; i+=3) {
372
+                byte r = curLine[i];
373
+                byte g = curLine[i+1];
374
+                byte b = curLine[i+2];
375
+                byte a = (byte)0xFF;
376
+                if(r==tr && g==tg && b==tb) {
377
+                    a = 0;
378
+                }
379
+                buffer.put(a).put(b).put(g).put(r);
380
+            }
381
+        } else {
382
+            for(int i=1,n=curLine.length ; i<n ; i+=3) {
383
+                buffer.put((byte)0xFF).put(curLine[i+2]).put(curLine[i+1]).put(curLine[i]);
384
+            }
385
+        }
386
+    }
387
+
388
+    private void copyRGBtoRGBA(ByteBuffer buffer, byte[] curLine) {
389
+        if(transPixel != null) {
390
+            byte tr = transPixel[1];
391
+            byte tg = transPixel[3];
392
+            byte tb = transPixel[5];
393
+            for(int i=1,n=curLine.length ; i<n ; i+=3) {
394
+                byte r = curLine[i];
395
+                byte g = curLine[i+1];
396
+                byte b = curLine[i+2];
397
+                byte a = (byte)0xFF;
398
+                if(r==tr && g==tg && b==tb) {
399
+                    a = 0;
400
+                }
401
+                buffer.put(r).put(g).put(b).put(a);
402
+            }
403
+        } else {
404
+            for(int i=1,n=curLine.length ; i<n ; i+=3) {
405
+                buffer.put(curLine[i]).put(curLine[i+1]).put(curLine[i+2]).put((byte)0xFF);
406
+            }
407
+        }
408
+    }
409
+
410
+    private void copyRGBtoBGRA(ByteBuffer buffer, byte[] curLine) {
411
+        if(transPixel != null) {
412
+            byte tr = transPixel[1];
413
+            byte tg = transPixel[3];
414
+            byte tb = transPixel[5];
415
+            for(int i=1,n=curLine.length ; i<n ; i+=3) {
416
+                byte r = curLine[i];
417
+                byte g = curLine[i+1];
418
+                byte b = curLine[i+2];
419
+                byte a = (byte)0xFF;
420
+                if(r==tr && g==tg && b==tb) {
421
+                    a = 0;
422
+                }
423
+                buffer.put(b).put(g).put(r).put(a);
424
+            }
425
+        } else {
426
+            for(int i=1,n=curLine.length ; i<n ; i+=3) {
427
+                buffer.put(curLine[i+2]).put(curLine[i+1]).put(curLine[i]).put((byte)0xFF);
428
+            }
429
+        }
430
+    }
431
+
432
+    private void copyRGBAtoABGR(ByteBuffer buffer, byte[] curLine) {
433
+        for(int i=1,n=curLine.length ; i<n ; i+=4) {
434
+            buffer.put(curLine[i+3]).put(curLine[i+2]).put(curLine[i+1]).put(curLine[i]);
435
+        }
436
+    }
437
+
438
+    private void copyRGBAtoBGRA(ByteBuffer buffer, byte[] curLine) {
439
+        for(int i=1,n=curLine.length ; i<n ; i+=4) {
440
+            buffer.put(curLine[i+2]).put(curLine[i+1]).put(curLine[i]).put(curLine[i+3]);
441
+        }
442
+    }
443
+
444
+    private void copyRGBAtoRGB(ByteBuffer buffer, byte[] curLine) {
445
+        for(int i=1,n=curLine.length ; i<n ; i+=4) {
446
+            buffer.put(curLine[i]).put(curLine[i+1]).put(curLine[i+2]);
447
+        }
448
+    }
449
+
450
+    private void copyPALtoABGR(ByteBuffer buffer, byte[] curLine) {
451
+        if(paletteA != null) {
452
+            for(int i=1,n=curLine.length ; i<n ; i+=1) {
453
+                int idx = curLine[i] & 255;
454
+                byte r = palette[idx*3 + 0];
455
+                byte g = palette[idx*3 + 1];
456
+                byte b = palette[idx*3 + 2];
457
+                byte a = paletteA[idx];
458
+                buffer.put(a).put(b).put(g).put(r);
459
+            }
460
+        } else {
461
+            for(int i=1,n=curLine.length ; i<n ; i+=1) {
462
+                int idx = curLine[i] & 255;
463
+                byte r = palette[idx*3 + 0];
464
+                byte g = palette[idx*3 + 1];
465
+                byte b = palette[idx*3 + 2];
466
+                byte a = (byte)0xFF;
467
+                buffer.put(a).put(b).put(g).put(r);
468
+            }
469
+        }
470
+    }
471
+
472
+    private void copyPALtoRGBA(ByteBuffer buffer, byte[] curLine) {
473
+        if(paletteA != null) {
474
+            for(int i=1,n=curLine.length ; i<n ; i+=1) {
475
+                int idx = curLine[i] & 255;
476
+                byte r = palette[idx*3 + 0];
477
+                byte g = palette[idx*3 + 1];
478
+                byte b = palette[idx*3 + 2];
479
+                byte a = paletteA[idx];
480
+                buffer.put(r).put(g).put(b).put(a);
481
+            }
482
+        } else {
483
+            for(int i=1,n=curLine.length ; i<n ; i+=1) {
484
+                int idx = curLine[i] & 255;
485
+                byte r = palette[idx*3 + 0];
486
+                byte g = palette[idx*3 + 1];
487
+                byte b = palette[idx*3 + 2];
488
+                byte a = (byte)0xFF;
489
+                buffer.put(r).put(g).put(b).put(a);
490
+            }
491
+        }
492
+    }
493
+    
494
+    private void copyPALtoRGB(ByteBuffer buffer, byte[] curLine) {
495
+        for(int i=1,n=curLine.length ; i<n ; i+=1) {
496
+            int idx = curLine[i] & 255;
497
+            byte r = palette[idx*3 + 0];
498
+            byte g = palette[idx*3 + 1];
499
+            byte b = palette[idx*3 + 2];
500
+            buffer.put(r).put(g).put(b);
501
+        }
502
+    }
503
+
504
+
505
+    private void copyPALtoBGRA(ByteBuffer buffer, byte[] curLine) {
506
+        if(paletteA != null) {
507
+            for(int i=1,n=curLine.length ; i<n ; i+=1) {
508
+                int idx = curLine[i] & 255;
509
+                byte r = palette[idx*3 + 0];
510
+                byte g = palette[idx*3 + 1];
511
+                byte b = palette[idx*3 + 2];
512
+                byte a = paletteA[idx];
513
+                buffer.put(b).put(g).put(r).put(a);
514
+            }
515
+        } else {
516
+            for(int i=1,n=curLine.length ; i<n ; i+=1) {
517
+                int idx = curLine[i] & 255;
518
+                byte r = palette[idx*3 + 0];
519
+                byte g = palette[idx*3 + 1];
520
+                byte b = palette[idx*3 + 2];
521
+                byte a = (byte)0xFF;
522
+                buffer.put(b).put(g).put(r).put(a);
523
+            }
524
+        }
525
+    }
526
+
527
+    private void expand4(byte[] src, byte[] dst) {
528
+        for(int i=1,n=dst.length ; i<n ; i+=2) {
529
+            int val = src[1 + (i >> 1)] & 255;
530
+            switch(n-i) {
531
+                default: dst[i+1] = (byte)(val & 15);
532
+                case 1:  dst[i  ] = (byte)(val >> 4);
533
+            }
534
+        }
535
+    }
536
+
537
+    private void expand2(byte[] src, byte[] dst) {
538
+        for(int i=1,n=dst.length ; i<n ; i+=4) {
539
+            int val = src[1 + (i >> 2)] & 255;
540
+            switch(n-i) {
541
+                default: dst[i+3] = (byte)((val     ) & 3);
542
+                case 3:  dst[i+2] = (byte)((val >> 2) & 3);
543
+                case 2:  dst[i+1] = (byte)((val >> 4) & 3);
544
+                case 1:  dst[i  ] = (byte)((val >> 6)    );
545
+            }
546
+        }
547
+    }
548
+
549
+    private void expand1(byte[] src, byte[] dst) {
550
+        for(int i=1,n=dst.length ; i<n ; i+=8) {
551
+            int val = src[1 + (i >> 3)] & 255;
552
+            switch(n-i) {
553
+                default: dst[i+7] = (byte)((val     ) & 1);
554
+                case 7:  dst[i+6] = (byte)((val >> 1) & 1);
555
+                case 6:  dst[i+5] = (byte)((val >> 2) & 1);
556
+                case 5:  dst[i+4] = (byte)((val >> 3) & 1);
557
+                case 4:  dst[i+3] = (byte)((val >> 4) & 1);
558
+                case 3:  dst[i+2] = (byte)((val >> 5) & 1);
559
+                case 2:  dst[i+1] = (byte)((val >> 6) & 1);
560
+                case 1:  dst[i  ] = (byte)((val >> 7)    );
561
+            }
562
+        }
563
+    }
564
+    
565
+    private void unfilter(byte[] curLine, byte[] prevLine) throws IOException {
566
+        switch (curLine[0]) {
567
+            case 0: // none
568
+                break;
569
+            case 1:
570
+                unfilterSub(curLine);
571
+                break;
572
+            case 2:
573
+                unfilterUp(curLine, prevLine);
574
+                break;
575
+            case 3:
576
+                unfilterAverage(curLine, prevLine);
577
+                break;
578
+            case 4:
579
+                unfilterPaeth(curLine, prevLine);
580
+                break;
581
+            default:
582
+                throw new IOException("invalide filter type in scanline: " + curLine[0]);
583
+        }
584
+    }
585
+    
586
+    private void unfilterSub(byte[] curLine) {
587
+        final int bpp = this.bytesPerPixel;
588
+        for(int i=bpp+1,n=curLine.length ; i<n ; ++i) {
589
+            curLine[i] += curLine[i-bpp];
590
+        }
591
+    }
592
+    
593
+    private void unfilterUp(byte[] curLine, byte[] prevLine) {
594
+        final int bpp = this.bytesPerPixel;
595
+        for(int i=1,n=curLine.length ; i<n ; ++i) {
596
+            curLine[i] += prevLine[i];
597
+        }
598
+    }
599
+    
600
+    private void unfilterAverage(byte[] curLine, byte[] prevLine) {
601
+        final int bpp = this.bytesPerPixel;
602
+        
603
+        int i;
604
+        for(i=1 ; i<=bpp ; ++i) {
605
+            curLine[i] += (byte)((prevLine[i] & 0xFF) >>> 1);
606
+        }
607
+        for(int n=curLine.length ; i<n ; ++i) {
608
+            curLine[i] += (byte)(((prevLine[i] & 0xFF) + (curLine[i - bpp] & 0xFF)) >>> 1);
609
+        }
610
+    }
611
+    
612
+    private void unfilterPaeth(byte[] curLine, byte[] prevLine) {
613
+        final int bpp = this.bytesPerPixel;
614
+        
615
+        int i;
616
+        for(i=1 ; i<=bpp ; ++i) {
617
+            curLine[i] += prevLine[i];
618
+        }
619
+        for(int n=curLine.length ; i<n ; ++i) {
620
+            int a = curLine[i - bpp] & 255;
621
+            int b = prevLine[i] & 255;
622
+            int c = prevLine[i - bpp] & 255;
623
+            int p = a + b - c;
624
+            int pa = p - a; if(pa < 0) pa = -pa;
625
+            int pb = p - b; if(pb < 0) pb = -pb;
626
+            int pc = p - c; if(pc < 0) pc = -pc;
627
+            if(pa<=pb && pa<=pc)
628
+                c = a;
629
+            else if(pb<=pc)
630
+                c = b;
631
+            curLine[i] += (byte)c;
632
+        }
633
+    }
634
+      
635
+    private void readIHDR() throws IOException {
636
+        checkChunkLength(13);
637
+        readChunk(buffer, 0, 13);
638
+        width = readInt(buffer, 0);
639
+        height = readInt(buffer, 4);
640
+        bitdepth = buffer[8] & 255;
641
+        colorType = buffer[9] & 255;
642
+        
643
+        switch (colorType) {
644
+        case COLOR_GREYSCALE:
645
+            if(bitdepth != 8) {
646
+                throw new IOException("Unsupported bit depth: " + bitdepth);
647
+            }
648
+            bytesPerPixel = 1;
649
+            break;
650
+        case COLOR_GREYALPHA:
651
+            if(bitdepth != 8) {
652
+                throw new IOException("Unsupported bit depth: " + bitdepth);
653
+            }
654
+            bytesPerPixel = 2;
655
+            break;
656
+        case COLOR_TRUECOLOR:
657
+            if(bitdepth != 8) {
658
+                throw new IOException("Unsupported bit depth: " + bitdepth);
659
+            }
660
+            bytesPerPixel = 3;
661
+            break;
662
+        case COLOR_TRUEALPHA:
663
+            if(bitdepth != 8) {
664
+                throw new IOException("Unsupported bit depth: " + bitdepth);
665
+            }
666
+            bytesPerPixel = 4;
667
+            break;
668
+        case COLOR_INDEXED:
669
+            switch(bitdepth) {
670
+            case 8:
671
+            case 4:
672
+            case 2:
673
+            case 1:
674
+                bytesPerPixel = 1;
675
+                break;
676
+            default:
677
+                throw new IOException("Unsupported bit depth: " + bitdepth);
678
+            }
679
+            break;
680
+        default:
681
+            throw new IOException("unsupported color format: " + colorType);
682
+        }
683
+        
684
+        if(buffer[10] != 0) {
685
+            throw new IOException("unsupported compression method");
686
+        }
687
+        if(buffer[11] != 0) {
688
+            throw new IOException("unsupported filtering method");
689
+        }
690
+        if(buffer[12] != 0) {
691
+            throw new IOException("unsupported interlace method");
692
+        }
693
+    }
694
+
695
+    private void readPLTE() throws IOException {
696
+        int paletteEntries = chunkLength / 3;
697
+        if(paletteEntries < 1 || paletteEntries > 256 || (chunkLength % 3) != 0) {
698
+            throw new IOException("PLTE chunk has wrong length");
699
+        }
700
+        palette = new byte[paletteEntries*3];
701
+        readChunk(palette, 0, palette.length);
702
+    }
703
+
704
+    private void readtRNS() throws IOException {
705
+        switch (colorType) {
706
+        case COLOR_GREYSCALE:
707
+            checkChunkLength(2);
708
+            transPixel = new byte[2];
709
+            readChunk(transPixel, 0, 2);
710
+            break;
711
+        case COLOR_TRUECOLOR:
712
+            checkChunkLength(6);
713
+            transPixel = new byte[6];
714
+            readChunk(transPixel, 0, 6);
715
+            break;
716
+        case COLOR_INDEXED:
717
+            if(palette == null) {
718
+                throw new IOException("tRNS chunk without PLTE chunk");
719
+            }
720
+            paletteA = new byte[palette.length/3];
721
+            Arrays.fill(paletteA, (byte)0xFF);
722
+            readChunk(paletteA, 0, paletteA.length);
723
+            break;
724
+        default:
725
+            // just ignore it
726
+        }
727
+    }
728
+    
729
+    private void closeChunk() throws IOException {
730
+        if(chunkRemaining > 0) {
731
+            // just skip the rest and the CRC
732
+            skip(chunkRemaining + 4);
733
+        } else {
734
+            readFully(buffer, 0, 4);
735
+            int expectedCrc = readInt(buffer, 0);
736
+            int computedCrc = (int)crc.getValue();
737
+            if(computedCrc != expectedCrc) {
738
+                throw new IOException("Invalid CRC");
739
+            }
740
+        }
741
+        chunkRemaining = 0;
742
+        chunkLength = 0;
743
+        chunkType = 0;
744
+    }
745
+    
746
+    private void openChunk() throws IOException {
747
+        readFully(buffer, 0, 8);
748
+        chunkLength = readInt(buffer, 0);
749
+        chunkType = readInt(buffer, 4);
750
+        chunkRemaining = chunkLength;
751
+        crc.reset();
752
+        crc.update(buffer, 4, 4);   // only chunkType
753
+    }
754
+    
755
+    private void openChunk(int expected) throws IOException {
756
+        openChunk();
757
+        if(chunkType != expected) {
758
+            throw new IOException("Expected chunk: " + Integer.toHexString(expected));
759
+        }
760
+    }
761
+
762
+    private void checkChunkLength(int expected) throws IOException {
763
+        if(chunkLength != expected) {
764
+            throw new IOException("Chunk has wrong size");
765
+        }
766
+    }
767
+    
768
+    private int readChunk(byte[] buffer, int offset, int length) throws IOException {
769
+        if(length > chunkRemaining) {
770
+            length = chunkRemaining;
771
+        }
772
+        readFully(buffer, offset, length);
773
+        crc.update(buffer, offset, length);
774
+        chunkRemaining -= length;
775
+        return length;
776
+    }
777
+
778
+    private void refillInflater(Inflater inflater) throws IOException {
779
+        while(chunkRemaining == 0) {
780
+            closeChunk();
781
+            openChunk(IDAT);
782
+        }
783
+        int read = readChunk(buffer, 0, buffer.length);
784
+        inflater.setInput(buffer, 0, read);
785
+    }
786
+    
787
+    private void readChunkUnzip(Inflater inflater, byte[] buffer, int offset, int length) throws IOException {
788
+        assert(buffer != this.buffer);
789
+        try {
790
+            do {
791
+                int read = inflater.inflate(buffer, offset, length);
792
+                if(read <= 0) {
793
+                    if(inflater.finished()) {
794
+                        throw new EOFException();
795
+                    }
796
+                    if(inflater.needsInput()) {
797
+                        refillInflater(inflater);
798
+                    } else {
799
+                        throw new IOException("Can't inflate " + length + " bytes");
800
+                    }
801
+                } else {
802
+                    offset += read;
803
+                    length -= read;
804
+                }
805
+            } while(length > 0);
806
+        } catch (DataFormatException ex) {
807
+            throw (IOException)(new IOException("inflate error").initCause(ex));
808
+        }
809
+    }
810
+
811
+    private void readFully(byte[] buffer, int offset, int length) throws IOException {
812
+        do {
813
+            int read = input.read(buffer, offset, length);
814
+            if(read < 0) {
815
+                throw new EOFException();
816
+            }
817
+            offset += read;
818
+            length -= read;
819
+        } while(length > 0);
820
+    }
821
+    
822
+    private int readInt(byte[] buffer, int offset) {
823
+        return
824
+                ((buffer[offset  ]      ) << 24) |
825
+                ((buffer[offset+1] & 255) << 16) |
826
+                ((buffer[offset+2] & 255) <<  8) |
827
+                ((buffer[offset+3] & 255)      );
828
+    }
829
+
830
+    private void skip(long amount) throws IOException {
831
+        while(amount > 0) {
832
+            long skipped = input.skip(amount);
833
+            if(skipped < 0) {
834
+                throw new EOFException();
835
+            }
836
+            amount -= skipped;
837
+        }
838
+    }
839
+    
840
+    private static boolean checkSignature(byte[] buffer) {
841
+        for(int i=0 ; i<SIGNATURE.length ; i++) {
842
+            if(buffer[i] != SIGNATURE[i]) {
843
+                return false;
844
+            }
845
+        }
846
+        return true;
847
+    }
848
+}
0 849
new file mode 100644
... ...
@@ -0,0 +1,63 @@
0
+/*
1
+ * Copyright (c) 2008-2012, Matthias Mann
2
+ *
3
+ * All rights reserved.
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ *     * Redistributions of source code must retain the above copyright notice,
9
+ *       this list of conditions and the following disclaimer.
10
+ *     * Redistributions in binary form must reproduce the above copyright
11
+ *       notice, this list of conditions and the following disclaimer in the
12
+ *       documentation and/or other materials provided with the distribution.
13
+ *     * Neither the name of Matthias Mann nor the names of its contributors may
14
+ *       be used to endorse or promote products derived from this software
15
+ *       without specific prior written permission.
16
+ *
17
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+ */
29
+package de.matthiasmann.jpegdecoder;
30
+
31
+import org.junit.Test;
32
+import static org.junit.Assert.*;
33
+
34
+/**
35
+ *
36
+ * @author Matthias Mann
37
+ */
38
+public class JpegTest {
39
+
40
+    public JpegTest() {
41
+    }
42
+
43
+    static final char dezigzagRef[] = {
44
+        0,  1,  8, 16,  9,  2,  3, 10,
45
+       17, 24, 32, 25, 18, 11,  4,  5,
46
+       12, 19, 26, 33, 40, 48, 41, 34,
47
+       27, 20, 13,  6,  7, 14, 21, 28,
48
+       35, 42, 49, 56, 57, 50, 43, 36,
49
+       29, 22, 15, 23, 30, 37, 44, 51,
50
+       58, 59, 52, 45, 38, 31, 39, 46,
51
+       53, 60, 61, 54, 47, 55, 62, 63,
52
+       // let corrupt input sample past end
53
+       63, 63, 63, 63, 63, 63, 63, 63,
54
+       63, 63, 63, 63, 63, 63, 63
55
+    };
56
+
57
+    @Test
58
+    public void testDezigzag() {
59
+        assertArrayEquals(dezigzagRef, JPEGDecoder.dezigzag);
60
+    }
61
+
62
+}
0 63
\ No newline at end of file
... ...
@@ -67,6 +67,7 @@ THE SOFTWARE. -->
67 67
         <module>playn</module>
68 68
         <module>playn-java</module>
69 69
         <module>playn-utils</module>
70
+        <module>pngdecoder</module>
70 71
         <module>smjpegdecoder</module>
71 72
         <module>samples</module>
72 73
         <module>tablelayout</module>
... ...
@@ -116,6 +117,11 @@ THE SOFTWARE. -->
116 116
             </dependency>
117 117
             <dependency>
118 118
                 <groupId>im.bci</groupId>
119
+                <artifactId>pngdecoder</artifactId>
120
+                <version>${project.version}</version>
121
+            </dependency>
122
+            <dependency>
123
+                <groupId>im.bci</groupId>
119 124
                 <artifactId>jpegdecoder</artifactId>
120 125
                 <version>${project.version}</version>
121 126
             </dependency>
... ...
@@ -140,11 +146,6 @@ THE SOFTWARE. -->
140 140
                 <version>0.4</version>
141 141
             </dependency>
142 142
             <dependency>
143
-                <groupId>org.l33tlabs.twl</groupId>
144
-                <artifactId>pngdecoder</artifactId>
145
-                <version>1.0</version>
146
-            </dependency>
147
-            <dependency>
148 143
                 <groupId>org.jcraft</groupId>
149 144
                 <artifactId>jorbis</artifactId>
150 145
                 <version>0.0.17</version>