Mercurial > projects > dwt-mac
comparison dwt/internal/image/PNGFileFormat.d @ 0:380af2bdd8e5
Upload of whole dwt tree
author | Jacob Carlborg <doob@me.com> <jacob.carlborg@gmail.com> |
---|---|
date | Sat, 09 Aug 2008 17:00:02 +0200 |
parents | |
children | b903c16b6f48 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:380af2bdd8e5 |
---|---|
1 /******************************************************************************* | |
2 * Copyright (c) 2000, 2006 IBM Corporation and others. | |
3 * All rights reserved. This program and the accompanying materials | |
4 * are made available under the terms of the Eclipse Public License v1.0 | |
5 * which accompanies this distribution, and is available at | |
6 * http://www.eclipse.org/legal/epl-v10.html | |
7 * | |
8 * Contributors: | |
9 * IBM Corporation - initial API and implementation | |
10 *******************************************************************************/ | |
11 module dwt.internal.image; | |
12 | |
13 | |
14 import java.io.IOException; | |
15 import java.io.InputStream; | |
16 | |
17 import dwt.DWT; | |
18 import dwt.graphics.ImageData; | |
19 import dwt.graphics.ImageLoader; | |
20 import dwt.graphics.ImageLoaderEvent; | |
21 import dwt.graphics.PaletteData; | |
22 import dwt.internal.Compatibility; | |
23 | |
24 public final class PNGFileFormat : FileFormat { | |
25 static final int SIGNATURE_LENGTH = 8; | |
26 static final int PRIME = 65521; | |
27 PngIhdrChunk headerChunk; | |
28 PngPlteChunk paletteChunk; | |
29 ImageData imageData; | |
30 byte[] data; | |
31 byte[] alphaPalette; | |
32 byte headerByte1; | |
33 byte headerByte2; | |
34 int adler; | |
35 | |
36 /** | |
37 * Skip over signature data. This has already been | |
38 * verified in isFileFormat(). | |
39 */ | |
40 void readSignature() throws IOException { | |
41 byte[] signature = new byte[SIGNATURE_LENGTH]; | |
42 inputStream.read(signature); | |
43 } | |
44 /** | |
45 * Load the PNG image from the byte stream. | |
46 */ | |
47 ImageData[] loadFromByteStream() { | |
48 try { | |
49 readSignature(); | |
50 PngChunkReader chunkReader = new PngChunkReader(inputStream); | |
51 headerChunk = chunkReader.getIhdrChunk(); | |
52 int width = headerChunk.getWidth(), height = headerChunk.getHeight(); | |
53 if (width <= 0 || height <= 0) DWT.error(DWT.ERROR_INVALID_IMAGE); | |
54 int imageSize = getAlignedBytesPerRow() * height; | |
55 data = new byte[imageSize]; | |
56 imageData = ImageData.internal_new( | |
57 width, | |
58 height, | |
59 headerChunk.getSwtBitsPerPixel(), | |
60 new PaletteData(0, 0, 0), | |
61 4, | |
62 data, | |
63 0, | |
64 null, | |
65 null, | |
66 -1, | |
67 -1, | |
68 DWT.IMAGE_PNG, | |
69 0, | |
70 0, | |
71 0, | |
72 0); | |
73 | |
74 if (headerChunk.usesDirectColor()) { | |
75 imageData.palette = headerChunk.getPaletteData(); | |
76 } | |
77 | |
78 // Read and process chunks until the IEND chunk is encountered. | |
79 while (chunkReader.hasMoreChunks()) { | |
80 readNextChunk(chunkReader); | |
81 } | |
82 | |
83 return new ImageData[] {imageData}; | |
84 } catch (IOException e) { | |
85 DWT.error(DWT.ERROR_INVALID_IMAGE); | |
86 return null; | |
87 } | |
88 } | |
89 /** | |
90 * Read and handle the next chunk of data from the | |
91 * PNG file. | |
92 */ | |
93 void readNextChunk(PngChunkReader chunkReader) throws IOException { | |
94 PngChunk chunk = chunkReader.readNextChunk(); | |
95 switch (chunk.getChunkType()) { | |
96 case PngChunk.CHUNK_IEND: | |
97 break; | |
98 case PngChunk.CHUNK_PLTE: | |
99 if (!headerChunk.usesDirectColor()) { | |
100 paletteChunk = (PngPlteChunk) chunk; | |
101 imageData.palette = paletteChunk.getPaletteData(); | |
102 } | |
103 break; | |
104 case PngChunk.CHUNK_tRNS: | |
105 PngTrnsChunk trnsChunk = (PngTrnsChunk) chunk; | |
106 if (trnsChunk.getTransparencyType(headerChunk) is | |
107 PngTrnsChunk.TRANSPARENCY_TYPE_PIXEL) | |
108 { | |
109 imageData.transparentPixel = | |
110 trnsChunk.getSwtTransparentPixel(headerChunk); | |
111 } else { | |
112 alphaPalette = trnsChunk.getAlphaValues(headerChunk, paletteChunk); | |
113 int transparentCount = 0, transparentPixel = -1; | |
114 for (int i = 0; i < alphaPalette.length; i++) { | |
115 if ((alphaPalette[i] & 0xFF) !is 255) { | |
116 transparentCount++; | |
117 transparentPixel = i; | |
118 } | |
119 } | |
120 if (transparentCount is 0) { | |
121 alphaPalette = null; | |
122 } else if (transparentCount is 1 && alphaPalette[transparentPixel] is 0) { | |
123 alphaPalette = null; | |
124 imageData.transparentPixel = transparentPixel; | |
125 } | |
126 } | |
127 break; | |
128 case PngChunk.CHUNK_IDAT: | |
129 if (chunkReader.readPixelData()) { | |
130 // All IDAT chunks in an image file must be | |
131 // sequential. If the pixel data has already | |
132 // been read and another IDAT block is encountered, | |
133 // then this is an invalid image. | |
134 DWT.error(DWT.ERROR_INVALID_IMAGE); | |
135 } else { | |
136 // Read in the pixel data for the image. This should | |
137 // go through all the image's IDAT chunks. | |
138 PngIdatChunk dataChunk = (PngIdatChunk) chunk; | |
139 readPixelData(dataChunk, chunkReader); | |
140 } | |
141 break; | |
142 default: | |
143 if (chunk.isCritical()) { | |
144 // All critical chunks must be supported. | |
145 DWT.error(DWT.ERROR_NOT_IMPLEMENTED); | |
146 } | |
147 } | |
148 } | |
149 void unloadIntoByteStream(ImageLoader loader) { | |
150 PngEncoder encoder = new PngEncoder(loader); | |
151 encoder.encode(outputStream); | |
152 } | |
153 bool isFileFormat(LEDataInputStream stream) { | |
154 try { | |
155 byte[] signature = new byte[SIGNATURE_LENGTH]; | |
156 stream.read(signature); | |
157 stream.unread(signature); | |
158 if ((signature[0] & 0xFF) !is 137) return false; //137 | |
159 if ((signature[1] & 0xFF) !is 80) return false; //P | |
160 if ((signature[2] & 0xFF) !is 78) return false; //N | |
161 if ((signature[3] & 0xFF) !is 71) return false; //G | |
162 if ((signature[4] & 0xFF) !is 13) return false; //<RETURN> | |
163 if ((signature[5] & 0xFF) !is 10) return false; //<LINEFEED> | |
164 if ((signature[6] & 0xFF) !is 26) return false; //<CTRL/Z> | |
165 if ((signature[7] & 0xFF) !is 10) return false; //<LINEFEED> | |
166 return true; | |
167 } catch (Exception e) { | |
168 return false; | |
169 } | |
170 } | |
171 /** | |
172 * DWT does not support 16-bit depths. If this image uses | |
173 * 16-bit depths, convert the data to an 8-bit depth. | |
174 */ | |
175 byte[] validateBitDepth(byte[] data) { | |
176 if (headerChunk.getBitDepth() > 8) { | |
177 byte[] result = new byte[data.length / 2]; | |
178 compress16BitDepthTo8BitDepth(data, 0, result, 0, result.length); | |
179 return result; | |
180 } else { | |
181 return data; | |
182 } | |
183 } | |
184 /** | |
185 * DWT does not support greyscale as a color type. For | |
186 * plain grayscale, we create a palette. For Grayscale | |
187 * with Alpha, however, we need to convert the pixels | |
188 * to use RGB values. | |
189 * Note: This method assumes that the bit depth of the | |
190 * data has already been restricted to 8 or less. | |
191 */ | |
192 void setPixelData(byte[] data, ImageData imageData) { | |
193 switch (headerChunk.getColorType()) { | |
194 case PngIhdrChunk.COLOR_TYPE_GRAYSCALE_WITH_ALPHA: | |
195 { | |
196 int width = imageData.width; | |
197 int height = imageData.height; | |
198 int destBytesPerLine = imageData.bytesPerLine; | |
199 /* | |
200 * If the image uses 16-bit depth, it is converted | |
201 * to an 8-bit depth image. | |
202 */ | |
203 int srcBytesPerLine = getAlignedBytesPerRow(); | |
204 if (headerChunk.getBitDepth() > 8) srcBytesPerLine /= 2; | |
205 | |
206 byte[] rgbData = new byte[destBytesPerLine * height]; | |
207 byte[] alphaData = new byte[width * height]; | |
208 for (int y = 0; y < height; y++) { | |
209 int srcIndex = srcBytesPerLine * y; | |
210 int destIndex = destBytesPerLine * y; | |
211 int destAlphaIndex = width * y; | |
212 for (int x = 0; x < width; x++) { | |
213 byte grey = data[srcIndex]; | |
214 byte alpha = data[srcIndex + 1]; | |
215 rgbData[destIndex + 0] = grey; | |
216 rgbData[destIndex + 1] = grey; | |
217 rgbData[destIndex + 2] = grey; | |
218 alphaData[destAlphaIndex] = alpha; | |
219 srcIndex += 2; | |
220 destIndex += 3; | |
221 destAlphaIndex++; | |
222 } | |
223 } | |
224 imageData.data = rgbData; | |
225 imageData.alphaData = alphaData; | |
226 break; | |
227 } | |
228 case PngIhdrChunk.COLOR_TYPE_RGB_WITH_ALPHA: | |
229 { | |
230 int width = imageData.width; | |
231 int height = imageData.height; | |
232 int destBytesPerLine = imageData.bytesPerLine; | |
233 int srcBytesPerLine = getAlignedBytesPerRow(); | |
234 /* | |
235 * If the image uses 16-bit depth, it is converted | |
236 * to an 8-bit depth image. | |
237 */ | |
238 if (headerChunk.getBitDepth() > 8) srcBytesPerLine /= 2; | |
239 | |
240 byte[] rgbData = new byte[destBytesPerLine * height]; | |
241 byte[] alphaData = new byte[width * height]; | |
242 for (int y = 0; y < height; y++) { | |
243 int srcIndex = srcBytesPerLine * y; | |
244 int destIndex = destBytesPerLine * y; | |
245 int destAlphaIndex = width * y; | |
246 for (int x = 0; x < width; x++) { | |
247 rgbData[destIndex + 0] = data[srcIndex + 0]; | |
248 rgbData[destIndex + 1] = data[srcIndex + 1]; | |
249 rgbData[destIndex + 2] = data[srcIndex + 2]; | |
250 alphaData[destAlphaIndex] = data[srcIndex + 3]; | |
251 srcIndex += 4; | |
252 destIndex += 3; | |
253 destAlphaIndex++; | |
254 } | |
255 } | |
256 imageData.data = rgbData; | |
257 imageData.alphaData = alphaData; | |
258 break; | |
259 } | |
260 case PngIhdrChunk.COLOR_TYPE_RGB: | |
261 imageData.data = data; | |
262 break; | |
263 case PngIhdrChunk.COLOR_TYPE_PALETTE: | |
264 imageData.data = data; | |
265 if (alphaPalette !is null) { | |
266 int size = imageData.width * imageData.height; | |
267 byte[] alphaData = new byte[size]; | |
268 byte[] pixelData = new byte[size]; | |
269 imageData.getPixels(0, 0, size, pixelData, 0); | |
270 for (int i = 0; i < pixelData.length; i++) { | |
271 alphaData[i] = alphaPalette[pixelData[i] & 0xFF]; | |
272 } | |
273 imageData.alphaData = alphaData; | |
274 } | |
275 break; | |
276 default: | |
277 imageData.data = data; | |
278 break; | |
279 } | |
280 } | |
281 /** | |
282 * PNG supports some color types and bit depths that are | |
283 * unsupported by DWT. If the image uses an unsupported | |
284 * color type (either of the gray scale types) or bit | |
285 * depth (16), convert the data to an DWT-supported | |
286 * format. Then assign the data into the ImageData given. | |
287 */ | |
288 void setImageDataValues(byte[] data, ImageData imageData) { | |
289 byte[] result = validateBitDepth(data); | |
290 setPixelData(result, imageData); | |
291 } | |
292 /** | |
293 * Read the image data from the data stream. This must handle | |
294 * decoding the data, filtering, and interlacing. | |
295 */ | |
296 void readPixelData(PngIdatChunk chunk, PngChunkReader chunkReader) throws IOException { | |
297 InputStream stream = new PngInputStream(chunk, chunkReader); | |
298 //TEMPORARY CODE | |
299 bool use3_2 = System.getProperty("dwt.internal.image.PNGFileFormat_3.2") !is null; | |
300 InputStream inflaterStream = use3_2 ? null : Compatibility.newInflaterInputStream(stream); | |
301 if (inflaterStream !is null) { | |
302 stream = inflaterStream; | |
303 } else { | |
304 stream = new PngDecodingDataStream(stream); | |
305 } | |
306 int interlaceMethod = headerChunk.getInterlaceMethod(); | |
307 if (interlaceMethod is PngIhdrChunk.INTERLACE_METHOD_NONE) { | |
308 readNonInterlacedImage(stream); | |
309 } else { | |
310 readInterlacedImage(stream); | |
311 } | |
312 /* | |
313 * InflaterInputStream does not consume all bytes in the stream | |
314 * when it is closed. This may leave unread IDAT chunks. The fix | |
315 * is to read all available bytes before closing it. | |
316 */ | |
317 while (stream.available() > 0) stream.read(); | |
318 stream.close(); | |
319 } | |
320 /** | |
321 * Answer the number of bytes in a word-aligned row of pixel data. | |
322 */ | |
323 int getAlignedBytesPerRow() { | |
324 return ((getBytesPerRow(headerChunk.getWidth()) + 3) / 4) * 4; | |
325 } | |
326 /** | |
327 * Answer the number of bytes in each row of the image | |
328 * data. Each PNG row is byte-aligned, so images with bit | |
329 * depths less than a byte may have unused bits at the | |
330 * end of each row. The value of these bits is undefined. | |
331 */ | |
332 int getBytesPerRow() { | |
333 return getBytesPerRow(headerChunk.getWidth()); | |
334 } | |
335 /** | |
336 * Answer the number of bytes needed to represent a pixel. | |
337 * This value depends on the image's color type and bit | |
338 * depth. | |
339 * Note that this method rounds up if an image's pixel size | |
340 * isn't byte-aligned. | |
341 */ | |
342 int getBytesPerPixel() { | |
343 int bitsPerPixel = headerChunk.getBitsPerPixel(); | |
344 return (bitsPerPixel + 7) / 8; | |
345 } | |
346 /** | |
347 * Answer the number of bytes in a row of the given pixel | |
348 * width. Each row is byte-aligned, so images with bit | |
349 * depths less than a byte may have unused bits at the | |
350 * end of each row. The value of these bits is undefined. | |
351 */ | |
352 int getBytesPerRow(int rowWidthInPixels) { | |
353 int bitsPerPixel = headerChunk.getBitsPerPixel(); | |
354 int bitsPerRow = bitsPerPixel * rowWidthInPixels; | |
355 int bitsPerByte = 8; | |
356 return (bitsPerRow + (bitsPerByte - 1)) / bitsPerByte; | |
357 } | |
358 /** | |
359 * 1. Read one of the seven frames of interlaced data. | |
360 * 2. Update the imageData. | |
361 * 3. Notify the image loader's listeners of the frame load. | |
362 */ | |
363 void readInterlaceFrame( | |
364 InputStream inputStream, | |
365 int rowInterval, | |
366 int columnInterval, | |
367 int startRow, | |
368 int startColumn, | |
369 int frameCount) throws IOException | |
370 { | |
371 int width = headerChunk.getWidth(); | |
372 int alignedBytesPerRow = getAlignedBytesPerRow(); | |
373 int height = headerChunk.getHeight(); | |
374 if (startRow >= height || startColumn >= width) return; | |
375 | |
376 int pixelsPerRow = (width - startColumn + columnInterval - 1) / columnInterval; | |
377 int bytesPerRow = getBytesPerRow(pixelsPerRow); | |
378 byte[] row1 = new byte[bytesPerRow]; | |
379 byte[] row2 = new byte[bytesPerRow]; | |
380 byte[] currentRow = row1; | |
381 byte[] lastRow = row2; | |
382 for (int row = startRow; row < height; row += rowInterval) { | |
383 byte filterType = (byte)inputStream.read(); | |
384 int read = 0; | |
385 while (read !is bytesPerRow) { | |
386 read += inputStream.read(currentRow, read, bytesPerRow - read); | |
387 } | |
388 filterRow(currentRow, lastRow, filterType); | |
389 if (headerChunk.getBitDepth() >= 8) { | |
390 int bytesPerPixel = getBytesPerPixel(); | |
391 int dataOffset = (row * alignedBytesPerRow) + (startColumn * bytesPerPixel); | |
392 for (int rowOffset = 0; rowOffset < currentRow.length; rowOffset += bytesPerPixel) { | |
393 for (int byteOffset = 0; byteOffset < bytesPerPixel; byteOffset++) { | |
394 data[dataOffset + byteOffset] = currentRow[rowOffset + byteOffset]; | |
395 } | |
396 dataOffset += (columnInterval * bytesPerPixel); | |
397 } | |
398 } else { | |
399 int bitsPerPixel = headerChunk.getBitDepth(); | |
400 int pixelsPerByte = 8 / bitsPerPixel; | |
401 int column = startColumn; | |
402 int rowBase = row * alignedBytesPerRow; | |
403 int valueMask = 0; | |
404 for (int i = 0; i < bitsPerPixel; i++) { | |
405 valueMask <<= 1; | |
406 valueMask |= 1; | |
407 } | |
408 int maxShift = 8 - bitsPerPixel; | |
409 for (int byteOffset = 0; byteOffset < currentRow.length; byteOffset++) { | |
410 for (int bitOffset = maxShift; bitOffset >= 0; bitOffset -= bitsPerPixel) { | |
411 if (column < width) { | |
412 int dataOffset = rowBase + (column * bitsPerPixel / 8); | |
413 int value = (currentRow[byteOffset] >> bitOffset) & valueMask; | |
414 int dataShift = maxShift - (bitsPerPixel * (column % pixelsPerByte)); | |
415 data[dataOffset] |= value << dataShift; | |
416 } | |
417 column += columnInterval; | |
418 } | |
419 } | |
420 } | |
421 currentRow = (currentRow is row1) ? row2 : row1; | |
422 lastRow = (lastRow is row1) ? row2 : row1; | |
423 } | |
424 setImageDataValues(data, imageData); | |
425 fireInterlacedFrameEvent(frameCount); | |
426 } | |
427 /** | |
428 * Read the pixel data for an interlaced image from the | |
429 * data stream. | |
430 */ | |
431 void readInterlacedImage(InputStream inputStream) throws IOException { | |
432 readInterlaceFrame(inputStream, 8, 8, 0, 0, 0); | |
433 readInterlaceFrame(inputStream, 8, 8, 0, 4, 1); | |
434 readInterlaceFrame(inputStream, 8, 4, 4, 0, 2); | |
435 readInterlaceFrame(inputStream, 4, 4, 0, 2, 3); | |
436 readInterlaceFrame(inputStream, 4, 2, 2, 0, 4); | |
437 readInterlaceFrame(inputStream, 2, 2, 0, 1, 5); | |
438 readInterlaceFrame(inputStream, 2, 1, 1, 0, 6); | |
439 } | |
440 /** | |
441 * Fire an event to let listeners know that an interlaced | |
442 * frame has been loaded. | |
443 * finalFrame should be true if the image has finished | |
444 * loading, false if there are more frames to come. | |
445 */ | |
446 void fireInterlacedFrameEvent(int frameCount) { | |
447 if (loader.hasListeners()) { | |
448 ImageData image = (ImageData) imageData.clone(); | |
449 bool finalFrame = frameCount is 6; | |
450 loader.notifyListeners(new ImageLoaderEvent(loader, image, frameCount, finalFrame)); | |
451 } | |
452 } | |
453 /** | |
454 * Read the pixel data for a non-interlaced image from the | |
455 * data stream. | |
456 * Update the imageData to reflect the new data. | |
457 */ | |
458 void readNonInterlacedImage(InputStream inputStream) throws IOException { | |
459 int dataOffset = 0; | |
460 int alignedBytesPerRow = getAlignedBytesPerRow(); | |
461 int bytesPerRow = getBytesPerRow(); | |
462 byte[] row1 = new byte[bytesPerRow]; | |
463 byte[] row2 = new byte[bytesPerRow]; | |
464 byte[] currentRow = row1; | |
465 byte[] lastRow = row2; | |
466 int height = headerChunk.getHeight(); | |
467 for (int row = 0; row < height; row++) { | |
468 byte filterType = (byte)inputStream.read(); | |
469 int read = 0; | |
470 while (read !is bytesPerRow) { | |
471 read += inputStream.read(currentRow, read, bytesPerRow - read); | |
472 } | |
473 filterRow(currentRow, lastRow, filterType); | |
474 System.arraycopy(currentRow, 0, data, dataOffset, bytesPerRow); | |
475 dataOffset += alignedBytesPerRow; | |
476 currentRow = (currentRow is row1) ? row2 : row1; | |
477 lastRow = (lastRow is row1) ? row2 : row1; | |
478 } | |
479 setImageDataValues(data, imageData); | |
480 } | |
481 /** | |
482 * DWT does not support 16-bit depth color formats. | |
483 * Convert the 16-bit data to 8-bit data. | |
484 * The correct way to do this is to multiply each | |
485 * 16 bit value by the value: | |
486 * (2^8 - 1) / (2^16 - 1). | |
487 * The fast way to do this is just to drop the low | |
488 * byte of the 16-bit value. | |
489 */ | |
490 static void compress16BitDepthTo8BitDepth( | |
491 byte[] source, | |
492 int sourceOffset, | |
493 byte[] destination, | |
494 int destinationOffset, | |
495 int numberOfValues) | |
496 { | |
497 //double multiplier = (Compatibility.pow2(8) - 1) / (Compatibility.pow2(16) - 1); | |
498 for (int i = 0; i < numberOfValues; i++) { | |
499 int sourceIndex = sourceOffset + (2 * i); | |
500 int destinationIndex = destinationOffset + i; | |
501 //int value = (source[sourceIndex] << 8) | source[sourceIndex + 1]; | |
502 //byte compressedValue = (byte)(value * multiplier); | |
503 byte compressedValue = source[sourceIndex]; | |
504 destination[destinationIndex] = compressedValue; | |
505 } | |
506 } | |
507 /** | |
508 * DWT does not support 16-bit depth color formats. | |
509 * Convert the 16-bit data to 8-bit data. | |
510 * The correct way to do this is to multiply each | |
511 * 16 bit value by the value: | |
512 * (2^8 - 1) / (2^16 - 1). | |
513 * The fast way to do this is just to drop the low | |
514 * byte of the 16-bit value. | |
515 */ | |
516 static int compress16BitDepthTo8BitDepth(int value) { | |
517 //double multiplier = (Compatibility.pow2(8) - 1) / (Compatibility.pow2(16) - 1); | |
518 //byte compressedValue = (byte)(value * multiplier); | |
519 return value >> 8; | |
520 } | |
521 /** | |
522 * PNG supports four filtering types. These types are applied | |
523 * per row of image data. This method unfilters the given row | |
524 * based on the filterType. | |
525 */ | |
526 void filterRow(byte[] row, byte[] previousRow, int filterType) { | |
527 int byteOffset = headerChunk.getFilterByteOffset(); | |
528 switch (filterType) { | |
529 case PngIhdrChunk.FILTER_NONE: | |
530 break; | |
531 case PngIhdrChunk.FILTER_SUB: | |
532 for (int i = byteOffset; i < row.length; i++) { | |
533 int current = row[i] & 0xFF; | |
534 int left = row[i - byteOffset] & 0xFF; | |
535 row[i] = (byte)((current + left) & 0xFF); | |
536 } | |
537 break; | |
538 case PngIhdrChunk.FILTER_UP: | |
539 for (int i = 0; i < row.length; i++) { | |
540 int current = row[i] & 0xFF; | |
541 int above = previousRow[i] & 0xFF; | |
542 row[i] = (byte)((current + above) & 0xFF); | |
543 } | |
544 break; | |
545 case PngIhdrChunk.FILTER_AVERAGE: | |
546 for (int i = 0; i < row.length; i++) { | |
547 int left = (i < byteOffset) ? 0 : row[i - byteOffset] & 0xFF; | |
548 int above = previousRow[i] & 0xFF; | |
549 int current = row[i] & 0xFF; | |
550 row[i] = (byte)((current + ((left + above) / 2)) & 0xFF); | |
551 } | |
552 break; | |
553 case PngIhdrChunk.FILTER_PAETH: | |
554 for (int i = 0; i < row.length; i++) { | |
555 int left = (i < byteOffset) ? 0 : row[i - byteOffset] & 0xFF; | |
556 int aboveLeft = (i < byteOffset) ? 0 : previousRow[i - byteOffset] & 0xFF; | |
557 int above = previousRow[i] & 0xFF; | |
558 | |
559 int a = Math.abs(above - aboveLeft); | |
560 int b = Math.abs(left - aboveLeft); | |
561 int c = Math.abs(left - aboveLeft + above - aboveLeft); | |
562 | |
563 int preductor = 0; | |
564 if (a <= b && a <= c) { | |
565 preductor = left; | |
566 } else if (b <= c) { | |
567 preductor = above; | |
568 } else { | |
569 preductor = aboveLeft; | |
570 } | |
571 | |
572 int currentValue = row[i] & 0xFF; | |
573 row[i] = (byte) ((currentValue + preductor) & 0xFF); | |
574 } | |
575 break; | |
576 } | |
577 } | |
578 | |
579 } |