author | prr |
Sat, 19 Sep 2015 15:45:59 -0700 | |
changeset 32865 | f9cb6e427f9e |
parent 25859 | 3317bb8137f4 |
child 37714 | 7a0b1c7e7054 |
permissions | -rw-r--r-- |
2 | 1 |
/* |
22636 | 2 |
* Copyright (c) 1999, 2014, Oracle and/or its affiliates. All rights reserved. |
2 | 3 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 |
* |
|
5 |
* This code is free software; you can redistribute it and/or modify it |
|
6 |
* under the terms of the GNU General Public License version 2 only, as |
|
5506 | 7 |
* published by the Free Software Foundation. Oracle designates this |
2 | 8 |
* particular file as subject to the "Classpath" exception as provided |
5506 | 9 |
* by Oracle in the LICENSE file that accompanied this code. |
2 | 10 |
* |
11 |
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 |
* version 2 for more details (a copy is included in the LICENSE file that |
|
15 |
* accompanied this code). |
|
16 |
* |
|
17 |
* You should have received a copy of the GNU General Public License version |
|
18 |
* 2 along with this work; if not, write to the Free Software Foundation, |
|
19 |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 |
* |
|
5506 | 21 |
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
22 |
* or visit www.oracle.com if you need additional information or have any |
|
23 |
* questions. |
|
2 | 24 |
*/ |
25 |
||
26 |
package sun.awt.image; |
|
27 |
||
28 |
import java.io.*; |
|
29 |
import java.util.*; |
|
30 |
import java.util.zip.*; |
|
31 |
import java.awt.image.*; |
|
32 |
import java.awt.Color; |
|
33 |
||
34 |
/** PNG - Portable Network Graphics - image file reader. |
|
5820
4f5e99470724
6967036: Need to fix links with // in Javadoc comments
ohair
parents:
5506
diff
changeset
|
35 |
See <a href=http://www.ietf.org/rfc/rfc2083.txt>RFC2083</a> for details. */ |
2 | 36 |
|
37 |
/* this is changed |
|
38 |
public class PNGImageDecoder extends FilterInputStream implements Runnable |
|
39 |
{ */ |
|
40 |
||
41 |
public class PNGImageDecoder extends ImageDecoder |
|
42 |
{ |
|
43 |
private static final int GRAY=0; |
|
44 |
private static final int PALETTE=1; |
|
45 |
private static final int COLOR=2; |
|
46 |
private static final int ALPHA=4; |
|
47 |
||
48 |
private static final int bKGDChunk = 0x624B4744; |
|
49 |
private static final int cHRMChunk = 0x6348524D; |
|
50 |
private static final int gAMAChunk = 0x67414D41; |
|
51 |
private static final int hISTChunk = 0x68495354; |
|
52 |
private static final int IDATChunk = 0x49444154; |
|
53 |
private static final int IENDChunk = 0x49454E44; |
|
54 |
private static final int IHDRChunk = 0x49484452; |
|
55 |
private static final int PLTEChunk = 0x504C5445; |
|
56 |
private static final int pHYsChunk = 0x70485973; |
|
57 |
private static final int sBITChunk = 0x73424954; |
|
58 |
private static final int tEXtChunk = 0x74455874; |
|
59 |
private static final int tIMEChunk = 0x74494D45; |
|
60 |
private static final int tRNSChunk = 0x74524E53; |
|
61 |
private static final int zTXtChunk = 0x7A545874; |
|
62 |
||
63 |
private int width; |
|
64 |
private int height; |
|
65 |
private int bitDepth; |
|
66 |
private int colorType; |
|
67 |
private int compressionMethod; |
|
68 |
private int filterMethod; |
|
69 |
private int interlaceMethod; |
|
70 |
private int gamma = 100000; |
|
24538
25bf8153fbfe
8039642: Fix raw and unchecked warnings in sun.awt.*
henryjen
parents:
22636
diff
changeset
|
71 |
private java.util.Hashtable<String, Object> properties; |
2 | 72 |
/* this is not needed |
73 |
ImageConsumer target; |
|
74 |
*/ |
|
75 |
private ColorModel cm; |
|
76 |
private byte[] red_map, green_map, blue_map, alpha_map; |
|
77 |
private int transparentPixel = -1; |
|
78 |
private byte[] transparentPixel_16 = null; // we need 6 bytes to store 16bpp value |
|
79 |
private static ColorModel greyModels[] = new ColorModel[4]; |
|
80 |
/* this is not needed |
|
81 |
PNGImageDecoder next; |
|
82 |
*/ |
|
83 |
||
84 |
private void property(String key,Object value) { |
|
85 |
if(value==null) return; |
|
24538
25bf8153fbfe
8039642: Fix raw and unchecked warnings in sun.awt.*
henryjen
parents:
22636
diff
changeset
|
86 |
if(properties==null) properties=new java.util.Hashtable<>(); |
2 | 87 |
properties.put(key,value); |
88 |
} |
|
89 |
private void property(String key,float value) { |
|
90 |
property(key,new Float(value)); |
|
91 |
} |
|
92 |
private final void pngassert(boolean b) throws IOException { |
|
93 |
if(!b) { |
|
94 |
PNGException e = new PNGException("Broken file"); |
|
95 |
e.printStackTrace(); |
|
96 |
throw e; |
|
97 |
} |
|
98 |
} |
|
99 |
protected boolean handleChunk(int key, byte[] buf, int st, int len) |
|
100 |
throws IOException { |
|
101 |
switch(key) { |
|
102 |
case bKGDChunk: |
|
103 |
Color c = null; |
|
104 |
switch(colorType) { |
|
105 |
case COLOR: |
|
106 |
case COLOR|ALPHA: |
|
107 |
pngassert(len==6); |
|
108 |
c = new Color(buf[st]&0xff,buf[st+2]&0xff,buf[st+4]&0xff); |
|
109 |
break; |
|
110 |
case COLOR|PALETTE: |
|
111 |
case COLOR|PALETTE|ALPHA: |
|
112 |
pngassert(len==1); |
|
113 |
int ix = buf[st]&0xFF; |
|
114 |
pngassert(red_map!=null && ix<red_map.length); |
|
115 |
c = new Color(red_map[ix]&0xff,green_map[ix]&0xff,blue_map[ix]&0xff); |
|
116 |
break; |
|
117 |
case GRAY: |
|
118 |
case GRAY|ALPHA: |
|
119 |
pngassert(len==2); |
|
120 |
int t = buf[st]&0xFF; |
|
121 |
c = new Color(t,t,t); |
|
122 |
break; |
|
123 |
} |
|
124 |
if(c!=null) property("background",c); |
|
125 |
break; |
|
126 |
case cHRMChunk: |
|
127 |
property("chromaticities", |
|
128 |
new Chromaticities( |
|
129 |
getInt(st), |
|
130 |
getInt(st+4), |
|
131 |
getInt(st+8), |
|
132 |
getInt(st+12), |
|
133 |
getInt(st+16), |
|
134 |
getInt(st+20), |
|
135 |
getInt(st+24), |
|
136 |
getInt(st+28))); |
|
137 |
break; |
|
138 |
case gAMAChunk: |
|
139 |
if(len!=4) throw new PNGException("bogus gAMA"); |
|
140 |
gamma = getInt(st); |
|
141 |
if(gamma!=100000) property("gamma",gamma/100000.0f); |
|
142 |
break; |
|
143 |
case hISTChunk: break; |
|
144 |
case IDATChunk: return false; |
|
145 |
case IENDChunk: break; |
|
146 |
case IHDRChunk: |
|
147 |
if(len!=13 |
|
148 |
||(width = getInt(st))==0 |
|
149 |
||(height = getInt(st+4))==0 |
|
150 |
) throw new PNGException("bogus IHDR"); |
|
151 |
bitDepth = getByte(st+8); |
|
152 |
colorType = getByte(st+9); |
|
153 |
compressionMethod = getByte(st+10); |
|
154 |
filterMethod = getByte(st+11); |
|
155 |
interlaceMethod = getByte(st+12); |
|
156 |
/* this is not needed |
|
157 |
if(target!=null) target.setDimensions(width,height); |
|
158 |
*/ |
|
159 |
break; |
|
160 |
case PLTEChunk: |
|
161 |
{ int tsize = len/3; |
|
162 |
red_map = new byte[tsize]; |
|
163 |
green_map = new byte[tsize]; |
|
164 |
blue_map = new byte[tsize]; |
|
165 |
for(int i=0,j=st; i<tsize; i++, j+=3) { |
|
166 |
red_map[i] = buf[j]; |
|
167 |
green_map[i] = buf[j+1]; |
|
168 |
blue_map[i] = buf[j+2]; |
|
169 |
} |
|
170 |
} |
|
171 |
break; |
|
172 |
case pHYsChunk: break; |
|
173 |
case sBITChunk: break; |
|
174 |
case tEXtChunk: |
|
175 |
int klen = 0; |
|
176 |
while(klen<len && buf[st+klen]!=0) klen++; |
|
177 |
if(klen<len) { |
|
178 |
String tkey = new String(buf,st,klen); |
|
179 |
String tvalue = new String(buf,st+klen+1,len-klen-1); |
|
180 |
property(tkey,tvalue); |
|
181 |
} |
|
182 |
break; |
|
183 |
case tIMEChunk: |
|
184 |
property("modtime",new GregorianCalendar( |
|
185 |
getShort(st+0), |
|
186 |
getByte(st+2)-1, |
|
187 |
getByte(st+3), |
|
188 |
getByte(st+4), |
|
189 |
getByte(st+5), |
|
190 |
getByte(st+6)).getTime()); |
|
191 |
break; |
|
192 |
case tRNSChunk: |
|
193 |
switch(colorType) { |
|
194 |
case PALETTE|COLOR: |
|
195 |
case PALETTE|COLOR|ALPHA: |
|
196 |
int alen = len; |
|
197 |
if(red_map!=null) alen = red_map.length; |
|
198 |
alpha_map = new byte[alen]; |
|
199 |
System.arraycopy(buf,st,alpha_map,0,len<alen ? len : alen); |
|
200 |
while (--alen>=len) alpha_map[alen] = (byte)0xFF; |
|
201 |
break; |
|
202 |
case COLOR: // doesn't deal with 16 bit colors properly |
|
203 |
case COLOR|ALPHA: // doesn't deal with 16 bit colors properly |
|
204 |
pngassert(len==6); |
|
205 |
if (bitDepth == 16) { |
|
206 |
transparentPixel_16 = new byte[6]; |
|
207 |
for (int i = 0; i < 6; i++) { |
|
208 |
transparentPixel_16[i] = (byte)getByte(st + i); |
|
209 |
} |
|
210 |
} else { |
|
211 |
transparentPixel = |
|
212 |
((getShort(st + 0)&0xFF)<<16) |
|
213 |
| ((getShort(st + 2)&0xFF)<< 8) |
|
214 |
| ((getShort(st + 4)&0xFF) ); |
|
215 |
} |
|
216 |
break; |
|
217 |
case GRAY: // doesn't deal with 16 bit colors properly |
|
218 |
case GRAY|ALPHA: // doesn't deal with 16 bit colors properly |
|
219 |
pngassert(len==2); |
|
220 |
/* REMIND: Discarding the LSB for 16 bit depth here |
|
221 |
* means that the all pixels which match the MSB |
|
222 |
* will be treated as transparent. |
|
223 |
*/ |
|
224 |
int t = getShort(st); |
|
225 |
t = 0xFF & ((bitDepth == 16) ? (t >> 8) : t); |
|
226 |
transparentPixel = (t<<16) | (t<< 8) | t; |
|
227 |
break; |
|
228 |
} |
|
229 |
break; |
|
230 |
case zTXtChunk: break; |
|
231 |
} |
|
232 |
return true; |
|
233 |
} |
|
22636 | 234 |
@SuppressWarnings("serial") // JDK-implementation class |
2 | 235 |
public class PNGException extends IOException { |
236 |
PNGException(String s) { super(s); } |
|
237 |
} |
|
238 |
/* this is changed |
|
239 |
public void run() { |
|
240 |
*/ |
|
241 |
public void produceImage() throws IOException, ImageFormatException { |
|
242 |
/* this is not needed |
|
243 |
ImageConsumer t = target; |
|
244 |
if(t!=null) try { |
|
245 |
*/ |
|
246 |
try { |
|
247 |
for(int i=0; i<signature.length; i++) |
|
248 |
if((signature[i]&0xFF)!=underlyingInputStream.read()) |
|
249 |
throw new PNGException("Chunk signature mismatch"); |
|
250 |
||
251 |
InputStream is = new BufferedInputStream(new InflaterInputStream(inputStream,new Inflater())); |
|
252 |
||
253 |
getData(); |
|
254 |
||
255 |
byte[] bPixels = null; |
|
256 |
int[] wPixels = null; |
|
257 |
int pixSize = width; |
|
258 |
int rowStride; |
|
259 |
int logDepth = 0; |
|
260 |
switch(bitDepth) { |
|
261 |
case 1: logDepth = 0; break; |
|
262 |
case 2: logDepth = 1; break; |
|
263 |
case 4: logDepth = 2; break; |
|
264 |
case 8: logDepth = 3; break; |
|
265 |
case 16: logDepth = 4; break; |
|
266 |
default: throw new PNGException("invalid depth"); |
|
267 |
} |
|
268 |
if(interlaceMethod!=0) {pixSize *= height;rowStride=width;} |
|
269 |
else rowStride = 0; |
|
270 |
int combinedType = colorType|(bitDepth<<3); |
|
271 |
int bitMask = (1<<(bitDepth>=8?8:bitDepth))-1; |
|
272 |
//Figure out the color model |
|
273 |
switch(colorType) { |
|
274 |
case COLOR|PALETTE: |
|
275 |
case COLOR|PALETTE|ALPHA: |
|
276 |
if(red_map==null) throw new PNGException("palette expected"); |
|
277 |
if(alpha_map==null) |
|
278 |
cm = new IndexColorModel(bitDepth,red_map.length, |
|
279 |
red_map,green_map,blue_map); |
|
280 |
else |
|
281 |
cm = new IndexColorModel(bitDepth,red_map.length, |
|
282 |
red_map,green_map,blue_map,alpha_map); |
|
283 |
bPixels = new byte[pixSize]; |
|
284 |
break; |
|
285 |
case GRAY: |
|
286 |
{ int llog = logDepth>=4 ? 3 : logDepth; |
|
287 |
if((cm=greyModels[llog]) == null) { |
|
288 |
int size = 1<<(1<<llog); |
|
289 |
||
290 |
byte ramp[] = new byte[size]; |
|
291 |
for(int i = 0; i<size; i++) ramp[i] = (byte)(255*i/(size-1)); |
|
292 |
||
293 |
if (transparentPixel == -1) { |
|
294 |
cm = new IndexColorModel(bitDepth,ramp.length,ramp,ramp,ramp); |
|
295 |
} else { |
|
296 |
cm = new IndexColorModel(bitDepth,ramp.length,ramp,ramp,ramp, |
|
297 |
(transparentPixel & 0xFF)); |
|
298 |
} |
|
299 |
greyModels[llog] = cm; |
|
300 |
} |
|
301 |
} |
|
302 |
bPixels = new byte[pixSize]; |
|
303 |
break; |
|
304 |
case COLOR: |
|
305 |
case COLOR|ALPHA: |
|
306 |
case GRAY|ALPHA: |
|
307 |
cm = ColorModel.getRGBdefault(); |
|
308 |
wPixels = new int[pixSize]; |
|
309 |
break; |
|
310 |
default: |
|
311 |
throw new PNGException("invalid color type"); |
|
312 |
} |
|
313 |
/* this is going to be set in the pixel store |
|
314 |
t.setColorModel(cm); |
|
315 |
t.setHints(interlaceMethod !=0 |
|
316 |
? ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES |
|
317 |
: ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES | |
|
318 |
ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME); |
|
319 |
*/ |
|
320 |
// code added to make it work with ImageDecoder architecture |
|
321 |
setDimensions(width, height); |
|
322 |
setColorModel(cm); |
|
323 |
int flags = (interlaceMethod !=0 |
|
324 |
? ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES |
|
325 |
: ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES | |
|
326 |
ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME); |
|
327 |
setHints(flags); |
|
328 |
headerComplete(); |
|
329 |
// end of adding |
|
330 |
||
331 |
int samplesPerPixel = ((colorType&PALETTE)!=0 ? 1 |
|
332 |
: ((colorType&COLOR)!=0 ? 3 : 1)+((colorType&ALPHA)!=0?1:0)); |
|
333 |
int bitsPerPixel = samplesPerPixel*bitDepth; |
|
334 |
int bytesPerPixel = (bitsPerPixel+7)>>3; |
|
335 |
int pass, passLimit; |
|
336 |
if(interlaceMethod==0) { pass = -1; passLimit = 0; } |
|
337 |
else { pass = 0; passLimit = 7; } |
|
338 |
// These loops are far from being tuned. They're this way to make them easy to |
|
339 |
// debug. Tuning comes later. |
|
340 |
/* code changed. target not needed here |
|
341 |
while(++pass<=passLimit && (t=target)!=null) { |
|
342 |
*/ |
|
343 |
while(++pass<=passLimit) { |
|
344 |
int row = startingRow[pass]; |
|
345 |
int rowInc = rowIncrement[pass]; |
|
346 |
int colInc = colIncrement[pass]; |
|
347 |
int bWidth = blockWidth[pass]; |
|
348 |
int bHeight = blockHeight[pass]; |
|
349 |
int sCol = startingCol[pass]; |
|
350 |
int rowPixelWidth = (width-sCol+(colInc-1))/colInc; |
|
351 |
int rowByteWidth = ((rowPixelWidth*bitsPerPixel)+7)>>3; |
|
352 |
if(rowByteWidth==0) continue; |
|
353 |
int pixelBufferInc = interlaceMethod==0 ? rowInc*width : 0; |
|
354 |
int rowOffset = rowStride*row; |
|
355 |
boolean firstRow = true; |
|
356 |
||
357 |
byte[] rowByteBuffer = new byte[rowByteWidth]; |
|
358 |
byte[] prevRowByteBuffer = new byte[rowByteWidth]; |
|
359 |
/* code changed. target not needed here |
|
360 |
while (row < height && (t=target)!=null) { |
|
361 |
*/ |
|
362 |
while (row < height) { |
|
363 |
int rowFilter = is.read(); |
|
364 |
for (int rowFillPos=0;rowFillPos<rowByteWidth; ) { |
|
365 |
int n = is.read(rowByteBuffer,rowFillPos,rowByteWidth-rowFillPos); |
|
366 |
if(n<=0) throw new PNGException("missing data"); |
|
367 |
rowFillPos+=n; |
|
368 |
} |
|
369 |
filterRow(rowByteBuffer, |
|
370 |
firstRow ? null : prevRowByteBuffer, |
|
371 |
rowFilter, rowByteWidth, bytesPerPixel); |
|
372 |
int col = sCol; |
|
373 |
int spos=0; |
|
374 |
int pixel = 0; |
|
375 |
while (col < width) { |
|
376 |
if(wPixels !=null) { |
|
377 |
switch(combinedType) { |
|
378 |
case COLOR|ALPHA|(8<<3): |
|
379 |
wPixels[col+rowOffset] = |
|
380 |
((rowByteBuffer[spos ]&0xFF)<<16) |
|
381 |
| ((rowByteBuffer[spos+1]&0xFF)<< 8) |
|
382 |
| ((rowByteBuffer[spos+2]&0xFF) ) |
|
383 |
| ((rowByteBuffer[spos+3]&0xFF)<<24); |
|
384 |
spos+=4; |
|
385 |
break; |
|
386 |
case COLOR|ALPHA|(16<<3): |
|
387 |
wPixels[col+rowOffset] = |
|
388 |
((rowByteBuffer[spos ]&0xFF)<<16) |
|
389 |
| ((rowByteBuffer[spos+2]&0xFF)<< 8) |
|
390 |
| ((rowByteBuffer[spos+4]&0xFF) ) |
|
391 |
| ((rowByteBuffer[spos+6]&0xFF)<<24); |
|
392 |
spos+=8; |
|
393 |
break; |
|
394 |
case COLOR|(8<<3): |
|
395 |
pixel = |
|
396 |
((rowByteBuffer[spos ]&0xFF)<<16) |
|
397 |
| ((rowByteBuffer[spos+1]&0xFF)<< 8) |
|
398 |
| ((rowByteBuffer[spos+2]&0xFF) ); |
|
399 |
if (pixel != transparentPixel) { |
|
400 |
pixel |= 0xff000000; |
|
401 |
} |
|
402 |
wPixels[col+rowOffset] = pixel; |
|
403 |
spos+=3; |
|
404 |
break; |
|
405 |
case COLOR|(16<<3): |
|
406 |
pixel = |
|
407 |
((rowByteBuffer[spos ]&0xFF)<<16) |
|
408 |
| ((rowByteBuffer[spos+2]&0xFF)<< 8) |
|
409 |
| ((rowByteBuffer[spos+4]&0xFF) ); |
|
410 |
||
411 |
boolean isTransparent = (transparentPixel_16 != null); |
|
412 |
for (int i = 0; isTransparent && (i < 6); i++) { |
|
413 |
isTransparent &= |
|
414 |
(rowByteBuffer[spos + i] & 0xFF) == (transparentPixel_16[i] & 0xFF); |
|
415 |
} |
|
416 |
if (!isTransparent) { |
|
417 |
pixel |= 0xff000000; |
|
418 |
} |
|
419 |
wPixels[col+rowOffset] = pixel; |
|
420 |
spos+=6; |
|
421 |
break; |
|
422 |
case GRAY|ALPHA|(8<<3): |
|
423 |
{ int tx = rowByteBuffer[spos]&0xFF; |
|
424 |
wPixels[col+rowOffset] = |
|
425 |
(tx<<16)|(tx<<8)|tx |
|
426 |
|((rowByteBuffer[spos+1]&0xFF)<<24); } |
|
427 |
spos+=2; |
|
428 |
break; |
|
429 |
case GRAY|ALPHA|(16<<3): |
|
430 |
{ int tx = rowByteBuffer[spos]&0xFF; |
|
431 |
wPixels[col+rowOffset] = |
|
432 |
(tx<<16)|(tx<<8)|tx |
|
433 |
|((rowByteBuffer[spos+2]&0xFF)<<24); } |
|
434 |
spos+=4; |
|
435 |
break; |
|
436 |
default: throw new PNGException("illegal type/depth"); |
|
437 |
} |
|
438 |
} else switch(bitDepth) { |
|
439 |
case 1: |
|
440 |
bPixels[col+rowOffset] = |
|
441 |
(byte)((rowByteBuffer[spos>>3]>>(7-(spos&7)))&1); |
|
442 |
spos++; |
|
443 |
break; |
|
444 |
case 2: |
|
445 |
bPixels[col+rowOffset] = |
|
446 |
(byte)((rowByteBuffer[spos>>2]>>((3-(spos&3))*2))&3); |
|
447 |
spos++; |
|
448 |
break; |
|
449 |
case 4: |
|
450 |
bPixels[col+rowOffset] = |
|
451 |
(byte)((rowByteBuffer[spos>>1]>>((1-(spos&1))*4))&15); |
|
452 |
spos++; |
|
453 |
break; |
|
454 |
case 8: bPixels[col+rowOffset] = rowByteBuffer[spos++]; |
|
455 |
break; |
|
456 |
case 16: bPixels[col+rowOffset] = rowByteBuffer[spos]; spos+=2; |
|
457 |
break; |
|
458 |
default: throw new PNGException("illegal type/depth"); |
|
459 |
} |
|
460 |
/*visit (row, col, |
|
461 |
min (bHeight, height - row), |
|
462 |
min (bWidth, width - col)); */ |
|
463 |
col += colInc; |
|
464 |
} |
|
465 |
if(interlaceMethod==0) |
|
466 |
if(wPixels!=null) { |
|
467 |
/* code changed. target not needed here |
|
468 |
t.setPixels(0,row,width,1,cm,wPixels,0,width); |
|
469 |
*/ |
|
470 |
// code added to make it work with ImageDecoder arch |
|
471 |
sendPixels(0,row,width,1,wPixels,0,width); |
|
472 |
// end of adding |
|
473 |
} |
|
474 |
else { |
|
475 |
/* code changed. target not needed here |
|
476 |
t.setPixels(0,row,width,1,cm,bPixels,0,width); |
|
477 |
*/ |
|
478 |
// code added to make it work with ImageDecoder arch |
|
479 |
sendPixels(0,row,width,1,bPixels,0,width); |
|
480 |
//end of adding |
|
481 |
} |
|
482 |
row += rowInc; |
|
483 |
rowOffset += rowInc*rowStride; |
|
484 |
byte[] T = rowByteBuffer; |
|
485 |
rowByteBuffer = prevRowByteBuffer; |
|
486 |
prevRowByteBuffer = T; |
|
487 |
firstRow = false; |
|
488 |
} |
|
489 |
if(interlaceMethod!=0) |
|
490 |
if(wPixels!=null) { |
|
491 |
/* code changed. target not needed here |
|
492 |
t.setPixels(0,0,width,height,cm,wPixels,0,width); |
|
493 |
*/ |
|
494 |
// code added to make it work with ImageDecoder arch |
|
495 |
sendPixels(0,0,width,height,wPixels,0,width); |
|
496 |
//end of adding |
|
497 |
} |
|
498 |
else { |
|
499 |
/* code changed. target not needed here |
|
500 |
t.setPixels(0,0,width,height,cm,bPixels,0,width); |
|
501 |
*/ |
|
502 |
// code added to make it work with ImageDecoder arch |
|
503 |
sendPixels(0,0,width,height,bPixels,0,width); |
|
504 |
//end of adding |
|
505 |
} |
|
506 |
} |
|
507 |
||
508 |
/* Here, the function "visit(row,column,height,width)" obtains the |
|
509 |
next transmitted pixel and paints a rectangle of the specified |
|
510 |
height and width, whose upper-left corner is at the specified row |
|
511 |
and column, using the color indicated by the pixel. Note that row |
|
512 |
and column are measured from 0,0 at the upper left corner. */ |
|
513 |
||
514 |
/* code not needed, don't deal with target |
|
515 |
if((t=target)!=null) { |
|
516 |
if(properties!=null) t.setProperties(properties); |
|
517 |
t.imageComplete(ImageConsumer.STATICIMAGEDONE); |
|
518 |
*/ |
|
519 |
||
520 |
imageComplete(ImageConsumer.STATICIMAGEDONE, true); |
|
521 |
||
522 |
/* code not needed } |
|
523 |
is.close(); |
|
524 |
*/ |
|
525 |
} catch(IOException e) { |
|
526 |
if(!aborted) { |
|
527 |
/* code not needed |
|
528 |
if((t=target)!=null) { |
|
529 |
PNGEncoder.prChunk(e.toString(),inbuf,pos,limit-pos,true); |
|
530 |
*/ |
|
531 |
property("error", e); |
|
532 |
/* code not needed |
|
533 |
t.setProperties(properties); |
|
534 |
t.imageComplete(ImageConsumer.IMAGEERROR|ImageConsumer.STATICIMAGEDONE); |
|
535 |
*/ |
|
536 |
imageComplete(ImageConsumer.IMAGEERROR|ImageConsumer.STATICIMAGEDONE, true); |
|
537 |
throw e; |
|
538 |
} |
|
539 |
} finally { |
|
540 |
try { close(); } catch(Throwable e){} |
|
541 |
/* code not needed |
|
542 |
target = null; |
|
543 |
endTurn(); |
|
544 |
*/ |
|
545 |
} |
|
546 |
} |
|
547 |
||
548 |
private boolean sendPixels(int x, int y, int w, int h, int[] pixels, |
|
549 |
int offset, int pixlength) { |
|
550 |
int count = setPixels(x, y, w, h, cm, |
|
551 |
pixels, offset, pixlength); |
|
552 |
if (count <= 0) { |
|
553 |
aborted = true; |
|
554 |
} |
|
555 |
return !aborted; |
|
556 |
} |
|
557 |
private boolean sendPixels(int x, int y, int w, int h, byte[] pixels, |
|
558 |
int offset, int pixlength) { |
|
559 |
int count = setPixels(x, y, w, h, cm, |
|
560 |
pixels, offset, pixlength); |
|
561 |
if (count <= 0) { |
|
562 |
aborted = true; |
|
563 |
} |
|
564 |
return !aborted; |
|
565 |
} |
|
566 |
||
567 |
private void filterRow(byte rowByteBuffer[], byte[] prevRow, |
|
568 |
int rowFilter, int rowByteWidth, int bytesPerSample) |
|
569 |
throws IOException { |
|
570 |
int x = 0; |
|
571 |
switch (rowFilter) { |
|
572 |
case 0: |
|
573 |
break; |
|
574 |
case 1: |
|
575 |
for (x = bytesPerSample; x < rowByteWidth; x++) |
|
576 |
rowByteBuffer[x] += rowByteBuffer[x - bytesPerSample]; |
|
577 |
break; |
|
578 |
case 2: |
|
579 |
if (prevRow != null) |
|
580 |
for ( ; x < rowByteWidth; x++) |
|
581 |
rowByteBuffer[x] += prevRow[x]; |
|
582 |
break; |
|
583 |
case 3: |
|
584 |
if (prevRow != null) { |
|
585 |
for ( ; x < bytesPerSample; x++) |
|
586 |
rowByteBuffer[x] += (0xff & prevRow[x])>>1; |
|
587 |
for ( ; x < rowByteWidth; x++) |
|
588 |
rowByteBuffer[x] += ((prevRow[x]&0xFF) + (rowByteBuffer[x - bytesPerSample]&0xFF))>>1; |
|
589 |
} else |
|
590 |
for (x = bytesPerSample; x < rowByteWidth; x++) |
|
591 |
rowByteBuffer[x] += (rowByteBuffer[x - bytesPerSample]&0xFF)>>1; |
|
592 |
break; |
|
593 |
case 4: |
|
594 |
if (prevRow != null) { |
|
595 |
for ( ; x < bytesPerSample; x++) |
|
596 |
rowByteBuffer[x] += prevRow[x]; |
|
597 |
for ( ; x < rowByteWidth; x++) { |
|
598 |
int a, b, c, p, pa, pb, pc, rval; |
|
599 |
a = rowByteBuffer[x - bytesPerSample]&0xFF; |
|
600 |
b = prevRow[x]&0xFF; |
|
601 |
c = prevRow[x - bytesPerSample]&0xFF; |
|
602 |
p = a + b - c; |
|
603 |
pa = p > a ? p - a : a - p; |
|
604 |
pb = p > b ? p - b : b - p; |
|
605 |
pc = p > c ? p - c : c - p; |
|
606 |
rowByteBuffer[x] += (pa <= pb) && (pa <= pc) ? a : pb <= pc ? b : c; |
|
607 |
} |
|
608 |
} else |
|
609 |
for (x = bytesPerSample; x < rowByteWidth; x++) |
|
610 |
rowByteBuffer[x] += rowByteBuffer[x - bytesPerSample]; |
|
611 |
break; |
|
612 |
default: |
|
613 |
throw new PNGException("Illegal filter"); |
|
614 |
} |
|
615 |
} |
|
616 |
private static final byte[] startingRow = { 0, 0, 0, 4, 0, 2, 0, 1 }; |
|
617 |
private static final byte[] startingCol = { 0, 0, 4, 0, 2, 0, 1, 0 }; |
|
618 |
private static final byte[] rowIncrement = { 1, 8, 8, 8, 4, 4, 2, 2 }; |
|
619 |
private static final byte[] colIncrement = { 1, 8, 8, 4, 4, 2, 2, 1 }; |
|
620 |
private static final byte[] blockHeight = { 1, 8, 8, 4, 4, 2, 2, 1 }; |
|
621 |
private static final byte[] blockWidth = { 1, 8, 4, 4, 2, 2, 1, 1 }; |
|
622 |
||
623 |
//abstract public class ChunkReader extends FilterInputStream { |
|
624 |
int pos, limit; |
|
625 |
int chunkStart; |
|
626 |
int chunkKey, chunkLength, chunkCRC; |
|
627 |
boolean seenEOF; |
|
628 |
||
629 |
private static final byte[] signature = { (byte) 137, (byte) 80, (byte) 78, |
|
630 |
(byte) 71, (byte) 13, (byte) 10, (byte) 26, (byte) 10 }; |
|
631 |
||
632 |
PNGFilterInputStream inputStream; |
|
633 |
InputStream underlyingInputStream; |
|
634 |
||
635 |
/* code changed |
|
636 |
public PNGImageDecoder(InputStream in, ImageConsumer t) throws IOException { |
|
637 |
*/ |
|
638 |
public PNGImageDecoder(InputStreamImageSource src, InputStream input) throws IOException { |
|
639 |
// code added |
|
640 |
super(src, input); |
|
641 |
inputStream = new PNGFilterInputStream(this, input); |
|
642 |
underlyingInputStream = inputStream.underlyingInputStream; |
|
643 |
// end of adding |
|
644 |
/* code changed |
|
645 |
super(in); |
|
646 |
target = t; |
|
647 |
waitTurn(); |
|
648 |
new Thread(this).start(); |
|
649 |
*/ |
|
650 |
} |
|
651 |
/* code changed to make it work with ImageDecoder architecture |
|
652 |
static int ThreadLimit = 10; |
|
32865
f9cb6e427f9e
8136783: Run blessed-modifier-order script on java.desktop
prr
parents:
25859
diff
changeset
|
653 |
private static synchronized void waitTurn() { |
2 | 654 |
try { |
655 |
while(ThreadLimit<=0) PNGImageDecoder.class.wait(1000); |
|
656 |
} catch(InterruptedException e){} |
|
657 |
ThreadLimit--; |
|
658 |
} |
|
32865
f9cb6e427f9e
8136783: Run blessed-modifier-order script on java.desktop
prr
parents:
25859
diff
changeset
|
659 |
private static synchronized void endTurn() { |
2 | 660 |
if(ThreadLimit<=0) PNGImageDecoder.class.notify(); |
661 |
ThreadLimit++; |
|
662 |
} |
|
663 |
*/ |
|
664 |
byte[] inbuf = new byte[4096]; |
|
665 |
private void fill() throws IOException { |
|
666 |
if(!seenEOF) { |
|
667 |
if(pos>0 && pos<limit) { |
|
668 |
System.arraycopy(inbuf,pos,inbuf,0,limit-pos); |
|
669 |
limit = limit-pos; |
|
670 |
pos = 0; |
|
671 |
} else if(pos>=limit) { |
|
672 |
pos = 0; limit = 0; |
|
673 |
} |
|
674 |
int bsize = inbuf.length; |
|
675 |
while(limit<bsize) { |
|
676 |
int n = underlyingInputStream.read(inbuf,limit,bsize-limit); |
|
677 |
if(n<=0) { seenEOF=true; break; } |
|
678 |
limit += n; |
|
679 |
} |
|
680 |
} |
|
681 |
} |
|
682 |
private boolean need(int n) throws IOException { |
|
683 |
if(limit-pos>=n) return true; |
|
684 |
fill(); |
|
685 |
if(limit-pos>=n) return true; |
|
686 |
if(seenEOF) return false; |
|
687 |
byte nin[] = new byte[n+100]; |
|
688 |
System.arraycopy(inbuf,pos,nin,0,limit-pos); |
|
689 |
limit = limit-pos; |
|
690 |
pos = 0; |
|
691 |
inbuf = nin; |
|
692 |
fill(); |
|
693 |
return limit-pos>=n; |
|
694 |
} |
|
695 |
private final int getInt(int pos) { |
|
696 |
return ((inbuf[pos ]&0xFF)<<24) |
|
697 |
| ((inbuf[pos+1]&0xFF)<<16) |
|
698 |
| ((inbuf[pos+2]&0xFF)<< 8) |
|
699 |
| ((inbuf[pos+3]&0xFF) ); |
|
700 |
} |
|
701 |
private final int getShort(int pos) { |
|
702 |
return (short)(((inbuf[pos ]&0xFF)<<8) |
|
703 |
| ((inbuf[pos+1]&0xFF) )); |
|
704 |
} |
|
705 |
private final int getByte(int pos) { |
|
706 |
return inbuf[pos]&0xFF; |
|
707 |
} |
|
708 |
private final boolean getChunk() throws IOException { |
|
709 |
chunkLength = 0; |
|
710 |
if (!need(8)) return false; |
|
711 |
chunkLength = getInt(pos); |
|
712 |
chunkKey = getInt(pos+4); |
|
713 |
if(chunkLength<0) throw new PNGException("bogus length: "+chunkLength); |
|
714 |
if (!need(chunkLength+12)) return false; |
|
715 |
chunkCRC = getInt(pos+8+chunkLength); |
|
716 |
chunkStart = pos+8; |
|
717 |
int calcCRC = crc(inbuf,pos+4,chunkLength+4); |
|
718 |
if(chunkCRC!=calcCRC && checkCRC) throw new PNGException("crc corruption"); |
|
719 |
pos+=chunkLength+12; |
|
720 |
return true; |
|
721 |
} |
|
722 |
private void readAll() throws IOException { |
|
723 |
while(getChunk()) handleChunk(chunkKey,inbuf,chunkStart,chunkLength); |
|
724 |
} |
|
725 |
boolean getData() throws IOException { |
|
726 |
while(chunkLength==0 && getChunk()) |
|
727 |
if(handleChunk(chunkKey,inbuf,chunkStart,chunkLength)) |
|
728 |
chunkLength = 0; |
|
729 |
return chunkLength>0; |
|
730 |
} |
|
731 |
//abstract protected boolean handleChunk(int key, byte[] buf, int st, int len) |
|
732 |
// throws IOException; |
|
733 |
private static boolean checkCRC = true; |
|
734 |
public static boolean getCheckCRC() { return checkCRC; } |
|
735 |
public static void setCheckCRC(boolean c) { checkCRC = c; } |
|
736 |
||
737 |
protected void wrc(int c) { |
|
738 |
c = c&0xFF; |
|
739 |
if(c<=' '||c>'z') c = '?'; |
|
740 |
System.out.write(c); |
|
741 |
} |
|
742 |
protected void wrk(int n) { |
|
743 |
wrc(n>>24); |
|
744 |
wrc(n>>16); |
|
745 |
wrc(n>>8); |
|
746 |
wrc(n); |
|
747 |
} |
|
748 |
public void print() { |
|
749 |
wrk(chunkKey); |
|
750 |
System.out.print(" "+chunkLength+"\n"); |
|
751 |
} |
|
752 |
||
753 |
/* Table of CRCs of all 8-bit messages. */ |
|
754 |
private static final int[] crc_table = new int[256]; |
|
755 |
||
756 |
/* Make the table for a fast CRC. */ |
|
757 |
static { |
|
758 |
for (int n = 0; n < 256; n++) { |
|
759 |
int c = n; |
|
760 |
for (int k = 0; k < 8; k++) |
|
761 |
if ((c & 1) != 0) |
|
762 |
c = 0xedb88320 ^ (c >>> 1); |
|
763 |
else |
|
764 |
c = c >>> 1; |
|
765 |
crc_table[n] = c; |
|
766 |
} |
|
767 |
} |
|
768 |
||
769 |
/* Update a running CRC with the bytes buf[0..len-1]--the CRC |
|
770 |
should be initialized to all 1's, and the transmitted value |
|
771 |
is the 1's complement of the final running CRC (see the |
|
772 |
crc() routine below)). */ |
|
773 |
||
32865
f9cb6e427f9e
8136783: Run blessed-modifier-order script on java.desktop
prr
parents:
25859
diff
changeset
|
774 |
private static int update_crc(int crc, byte[] buf, int offset, int len) { |
2 | 775 |
int c = crc; |
776 |
while (--len>=0) |
|
777 |
c = crc_table[(c ^ buf[offset++]) & 0xff] ^ (c >>> 8); |
|
778 |
return c; |
|
779 |
} |
|
780 |
||
781 |
/* Return the CRC of the bytes buf[0..len-1]. */ |
|
32865
f9cb6e427f9e
8136783: Run blessed-modifier-order script on java.desktop
prr
parents:
25859
diff
changeset
|
782 |
private static int crc(byte[] buf, int offset, int len) { |
2 | 783 |
return update_crc(0xffffffff, buf, offset, len) ^ 0xffffffff; |
784 |
} |
|
785 |
public static class Chromaticities { |
|
786 |
public float whiteX, whiteY, redX, redY, greenX, greenY, blueX, blueY; |
|
787 |
Chromaticities(int wx, int wy, int rx, int ry, int gx, int gy, int bx, int by) { |
|
788 |
whiteX = wx/100000.0f; |
|
789 |
whiteY = wy/100000.0f; |
|
790 |
redX = rx/100000.0f; |
|
791 |
redY = ry/100000.0f; |
|
792 |
greenX = gx/100000.0f; |
|
793 |
greenY = gy/100000.0f; |
|
794 |
blueX = bx/100000.0f; |
|
795 |
blueY = by/100000.0f; |
|
796 |
} |
|
797 |
public String toString() { |
|
798 |
return "Chromaticities(white="+whiteX+","+whiteY+";red="+ |
|
799 |
redX+","+redY+";green="+ |
|
800 |
greenX+","+greenY+";blue="+ |
|
801 |
blueX+","+blueY+")"; |
|
802 |
} |
|
803 |
} |
|
804 |
} |
|
805 |
||
806 |
// the following class are added to make it work with ImageDecoder architecture |
|
807 |
||
808 |
class PNGFilterInputStream extends FilterInputStream { |
|
809 |
PNGImageDecoder owner; |
|
810 |
public InputStream underlyingInputStream; |
|
811 |
public PNGFilterInputStream(PNGImageDecoder owner, InputStream is) { |
|
812 |
super(is); |
|
813 |
underlyingInputStream = in; |
|
814 |
this.owner = owner; |
|
815 |
} |
|
816 |
||
817 |
public int available() throws IOException { |
|
818 |
return owner.limit-owner.pos+in.available();} |
|
819 |
public boolean markSupported() { return false; } |
|
820 |
public int read() throws IOException { |
|
821 |
if(owner.chunkLength<=0) if(!owner.getData()) return -1; |
|
822 |
owner.chunkLength--; |
|
823 |
return owner.inbuf[owner.chunkStart++]&0xFF; |
|
824 |
} |
|
825 |
public int read(byte[] b) throws IOException{return read(b,0,b.length);} |
|
826 |
public int read(byte[] b, int st, int len) throws IOException { |
|
827 |
if(owner.chunkLength<=0) if(!owner.getData()) return -1; |
|
828 |
if(owner.chunkLength<len) len = owner.chunkLength; |
|
829 |
System.arraycopy(owner.inbuf,owner.chunkStart,b,st,len); |
|
830 |
owner.chunkLength-=len; |
|
831 |
owner.chunkStart+=len; |
|
832 |
return len; |
|
833 |
} |
|
834 |
public long skip(long n) throws IOException { |
|
835 |
int i; |
|
836 |
for(i = 0; i<n && read()>=0; i++); |
|
837 |
return i; |
|
838 |
} |
|
839 |
||
840 |
||
841 |
} |