comparison dwt/internal/image/PNGFileFormat.d @ 34:5123b17c98ef

Ported dwt.events.*, dwt.graphics.GC, Region, dwt.internal.image.*
author Jacob Carlborg <doob@me.com> <jacob.carlborg@gmail.com>
date Sun, 14 Sep 2008 01:45:57 +0200
parents e831403a80a9
children d8635bb48c7c
comparison
equal deleted inserted replaced
33:965ac0a77267 34:5123b17c98ef
1 /******************************************************************************* 1 /*******************************************************************************
2 * Copyright (c) 2000, 2006 IBM Corporation and others. 2 * Copyright (c) 2000, 2008 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials 3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0 4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at 5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html 6 * http://www.eclipse.org/legal/epl-v10.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * IBM Corporation - initial API and implementation 9 * IBM Corporation - initial API and implementation
10 * Port to the D programming language:
11 * Frank Benoit <benoit@tionex.de>
10 *******************************************************************************/ 12 *******************************************************************************/
11 module dwt.internal.image; 13 module dwt.internal.image.PNGFileFormat;
12 14
13
14 import java.io.IOException;
15 import java.io.InputStream;
16 15
17 import dwt.DWT; 16 import dwt.DWT;
18 import dwt.graphics.ImageData; 17 import dwt.graphics.ImageData;
18 import dwt.graphics.ImageLoaderEvent;
19 import dwt.graphics.ImageLoader; 19 import dwt.graphics.ImageLoader;
20 import dwt.graphics.ImageLoaderEvent;
21 import dwt.graphics.PaletteData; 20 import dwt.graphics.PaletteData;
22 import dwt.internal.Compatibility; 21 import dwt.internal.Compatibility;
23 22 import dwt.internal.image.FileFormat;
24 public final class PNGFileFormat : FileFormat { 23 import dwt.internal.image.PngIhdrChunk;
24 import dwt.internal.image.PngPlteChunk;
25 import dwt.internal.image.PngChunkReader;
26 import dwt.internal.image.PngChunk;
27 import dwt.internal.image.PngTrnsChunk;
28 import dwt.internal.image.PngIdatChunk;
29 import dwt.internal.image.PngEncoder;
30 import dwt.internal.image.PngInputStream;
31 import dwt.internal.image.PngDecodingDataStream;
32 import dwt.dwthelper.utils;
33
34 import dwt.dwthelper.BufferedInputStream;
35
36 import tango.core.Exception;
37
38 final class PNGFileFormat : FileFormat {
25 static final int SIGNATURE_LENGTH = 8; 39 static final int SIGNATURE_LENGTH = 8;
26 static final int PRIME = 65521; 40 static final int PRIME = 65521;
27 PngIhdrChunk headerChunk; 41 PngIhdrChunk headerChunk;
28 PngPlteChunk paletteChunk; 42 PngPlteChunk paletteChunk;
29 ImageData imageData; 43 ImageData imageData;
33 byte headerByte2; 47 byte headerByte2;
34 int adler; 48 int adler;
35 49
36 /** 50 /**
37 * Skip over signature data. This has already been 51 * Skip over signature data. This has already been
38 * verified in isFileFormat(). 52 * verified in isFileFormat().
39 */ 53 */
40 void readSignature() { 54 void readSignature() {
41 byte[] signature = new byte[SIGNATURE_LENGTH]; 55 byte[] signature = new byte[SIGNATURE_LENGTH];
42 inputStream.read(signature); 56 inputStream.read(signature);
43 } 57 }
44 /** 58 /**
45 * Load the PNG image from the byte stream. 59 * Load the PNG image from the byte stream.
46 */ 60 */
47 ImageData[] loadFromByteStream() { 61 override ImageData[] loadFromByteStream() {
48 try { 62 try {
49 readSignature(); 63 readSignature();
50 PngChunkReader chunkReader = new PngChunkReader(inputStream); 64 PngChunkReader chunkReader = new PngChunkReader(inputStream);
51 headerChunk = chunkReader.getIhdrChunk(); 65 headerChunk = chunkReader.getIhdrChunk();
52 int width = headerChunk.getWidth(), height = headerChunk.getHeight(); 66 int width = headerChunk.getWidth(), height = headerChunk.getHeight();
53 if (width <= 0 || height <= 0) DWT.error(DWT.ERROR_INVALID_IMAGE); 67 if (width <= 0 || height <= 0) DWT.error(DWT.ERROR_INVALID_IMAGE);
54 int imageSize = getAlignedBytesPerRow() * height; 68 int imageSize = getAlignedBytesPerRow() * height;
55 data = new byte[imageSize]; 69 data = new byte[imageSize];
56 imageData = ImageData.internal_new( 70 imageData = ImageData.internal_new(
57 width, 71 width,
58 height, 72 height,
59 headerChunk.getSwtBitsPerPixel(), 73 headerChunk.getSwtBitsPerPixel(),
60 new PaletteData(0, 0, 0), 74 new PaletteData(0, 0, 0),
67 -1, 81 -1,
68 DWT.IMAGE_PNG, 82 DWT.IMAGE_PNG,
69 0, 83 0,
70 0, 84 0,
71 0, 85 0,
72 0); 86 0);
73 87
74 if (headerChunk.usesDirectColor()) { 88 if (headerChunk.usesDirectColor()) {
75 imageData.palette = headerChunk.getPaletteData(); 89 imageData.palette = headerChunk.getPaletteData();
76 } 90 }
77 91
78 // Read and process chunks until the IEND chunk is encountered. 92 // Read and process chunks until the IEND chunk is encountered.
79 while (chunkReader.hasMoreChunks()) { 93 while (chunkReader.hasMoreChunks()) {
80 readNextChunk(chunkReader); 94 readNextChunk(chunkReader);
81 } 95 }
82 96
83 return new ImageData[] {imageData}; 97 return [imageData];
84 } catch (IOException e) { 98 } catch (IOException e) {
85 DWT.error(DWT.ERROR_INVALID_IMAGE); 99 DWT.error(DWT.ERROR_INVALID_IMAGE);
86 return null; 100 return null;
87 } 101 }
88 } 102 }
89 /** 103 /**
90 * Read and handle the next chunk of data from the 104 * Read and handle the next chunk of data from the
91 * PNG file. 105 * PNG file.
92 */ 106 */
93 void readNextChunk(PngChunkReader chunkReader) { 107 void readNextChunk(PngChunkReader chunkReader) {
94 PngChunk chunk = chunkReader.readNextChunk(); 108 PngChunk chunk = chunkReader.readNextChunk();
95 switch (chunk.getChunkType()) { 109 switch (chunk.getChunkType()) {
96 case PngChunk.CHUNK_IEND: 110 case PngChunk.CHUNK_IEND:
97 break; 111 break;
98 case PngChunk.CHUNK_PLTE: 112 case PngChunk.CHUNK_PLTE:
99 if (!headerChunk.usesDirectColor()) { 113 if (!headerChunk.usesDirectColor()) {
100 paletteChunk = cast(PngPlteChunk) chunk; 114 paletteChunk = cast(PngPlteChunk) chunk;
101 imageData.palette = paletteChunk.getPaletteData(); 115 imageData.palette = paletteChunk.getPaletteData();
102 } 116 }
103 break; 117 break;
104 case PngChunk.CHUNK_tRNS: 118 case PngChunk.CHUNK_tRNS:
105 PngTrnsChunk trnsChunk = cast(PngTrnsChunk) chunk; 119 PngTrnsChunk trnsChunk = cast(PngTrnsChunk) chunk;
106 if (trnsChunk.getTransparencyType(headerChunk) is 120 if (trnsChunk.getTransparencyType(headerChunk) is
107 PngTrnsChunk.TRANSPARENCY_TYPE_PIXEL) 121 PngTrnsChunk.TRANSPARENCY_TYPE_PIXEL)
108 { 122 {
109 imageData.transparentPixel = 123 imageData.transparentPixel =
110 trnsChunk.getSwtTransparentPixel(headerChunk); 124 trnsChunk.getSwtTransparentPixel(headerChunk);
111 } else { 125 } else {
112 alphaPalette = trnsChunk.getAlphaValues(headerChunk, paletteChunk); 126 alphaPalette = trnsChunk.getAlphaValues(headerChunk, paletteChunk);
113 int transparentCount = 0, transparentPixel = -1; 127 int transparentCount = 0, transparentPixel = -1;
114 for (int i = 0; i < alphaPalette.length; i++) { 128 for (int i = 0; i < alphaPalette.length; i++) {
132 // been read and another IDAT block is encountered, 146 // been read and another IDAT block is encountered,
133 // then this is an invalid image. 147 // then this is an invalid image.
134 DWT.error(DWT.ERROR_INVALID_IMAGE); 148 DWT.error(DWT.ERROR_INVALID_IMAGE);
135 } else { 149 } else {
136 // Read in the pixel data for the image. This should 150 // Read in the pixel data for the image. This should
137 // go through all the image's IDAT chunks. 151 // go through all the image's IDAT chunks.
138 PngIdatChunk dataChunk = cast(PngIdatChunk) chunk; 152 PngIdatChunk dataChunk = cast(PngIdatChunk) chunk;
139 readPixelData(dataChunk, chunkReader); 153 readPixelData(dataChunk, chunkReader);
140 } 154 }
141 break; 155 break;
142 default: 156 default:
143 if (chunk.isCritical()) { 157 if (chunk.isCritical()) {
144 // All critical chunks must be supported. 158 // All critical chunks must be supported.
145 DWT.error(DWT.ERROR_NOT_IMPLEMENTED); 159 DWT.error(DWT.ERROR_NOT_IMPLEMENTED);
146 } 160 }
147 } 161 }
148 } 162 }
149 void unloadIntoByteStream(ImageLoader loader) { 163 override void unloadIntoByteStream(ImageLoader loader) {
150 PngEncoder encoder = new PngEncoder(loader); 164 PngEncoder encoder = new PngEncoder(loader);
151 encoder.encode(outputStream); 165 encoder.encode(outputStream);
152 } 166 }
153 bool isFileFormat(LEDataInputStream stream) { 167 override bool isFileFormat(LEDataInputStream stream) {
154 try { 168 try {
155 byte[] signature = new byte[SIGNATURE_LENGTH]; 169 byte[] signature = new byte[SIGNATURE_LENGTH];
156 stream.read(signature); 170 stream.read(signature);
157 stream.unread(signature); 171 stream.unread(signature);
158 if ((signature[0] & 0xFF) !is 137) return false; //137 172 if ((signature[0] & 0xFF) !is 137) return false; //137
160 if ((signature[2] & 0xFF) !is 78) return false; //N 174 if ((signature[2] & 0xFF) !is 78) return false; //N
161 if ((signature[3] & 0xFF) !is 71) return false; //G 175 if ((signature[3] & 0xFF) !is 71) return false; //G
162 if ((signature[4] & 0xFF) !is 13) return false; //<RETURN> 176 if ((signature[4] & 0xFF) !is 13) return false; //<RETURN>
163 if ((signature[5] & 0xFF) !is 10) return false; //<LINEFEED> 177 if ((signature[5] & 0xFF) !is 10) return false; //<LINEFEED>
164 if ((signature[6] & 0xFF) !is 26) return false; //<CTRL/Z> 178 if ((signature[6] & 0xFF) !is 26) return false; //<CTRL/Z>
165 if ((signature[7] & 0xFF) !is 10) return false; //<LINEFEED> 179 if ((signature[7] & 0xFF) !is 10) return false; //<LINEFEED>
166 return true; 180 return true;
167 } catch (Exception e) { 181 } catch (Exception e) {
168 return false; 182 return false;
169 } 183 }
170 } 184 }
254 } 268 }
255 } 269 }
256 imageData.data = rgbData; 270 imageData.data = rgbData;
257 imageData.alphaData = alphaData; 271 imageData.alphaData = alphaData;
258 break; 272 break;
259 } 273 }
260 case PngIhdrChunk.COLOR_TYPE_RGB: 274 case PngIhdrChunk.COLOR_TYPE_RGB:
261 imageData.data = data; 275 imageData.data = data;
262 break; 276 break;
263 case PngIhdrChunk.COLOR_TYPE_PALETTE: 277 case PngIhdrChunk.COLOR_TYPE_PALETTE:
264 imageData.data = data; 278 imageData.data = data;
277 imageData.data = data; 291 imageData.data = data;
278 break; 292 break;
279 } 293 }
280 } 294 }
281 /** 295 /**
282 * PNG supports some color types and bit depths that are 296 * PNG supports some color types and bit depths that are
283 * unsupported by DWT. If the image uses an unsupported 297 * unsupported by DWT. If the image uses an unsupported
284 * color type (either of the gray scale types) or bit 298 * color type (either of the gray scale types) or bit
285 * depth (16), convert the data to an DWT-supported 299 * depth (16), convert the data to an DWT-supported
286 * format. Then assign the data into the ImageData given. 300 * format. Then assign the data into the ImageData given.
287 */ 301 */
291 } 305 }
292 /** 306 /**
293 * Read the image data from the data stream. This must handle 307 * Read the image data from the data stream. This must handle
294 * decoding the data, filtering, and interlacing. 308 * decoding the data, filtering, and interlacing.
295 */ 309 */
296 void readPixelData(PngIdatChunk chunk, PngChunkReader chunkReader) { 310 void readPixelData(PngIdatChunk chunk, PngChunkReader chunkReader) {
297 InputStream stream = new PngInputStream(chunk, chunkReader); 311 InputStream stream = new PngInputStream(chunk, chunkReader);
298 //TEMPORARY CODE 312 //TEMPORARY CODE
299 bool use3_2 = System.getProperty("dwt.internal.image.PNGFileFormat_3.2") !is null; 313 //PORTING_FIXME
314 bool use3_2 = true;//System.getProperty("dwt.internal.image.PNGFileFormat_3.2") !is null;
300 InputStream inflaterStream = use3_2 ? null : Compatibility.newInflaterInputStream(stream); 315 InputStream inflaterStream = use3_2 ? null : Compatibility.newInflaterInputStream(stream);
301 if (inflaterStream !is null) { 316 if (inflaterStream !is null) {
302 stream = inflaterStream; 317 stream = inflaterStream;
303 } else { 318 } else {
304 stream = new PngDecodingDataStream(stream); 319 stream = new PngDecodingDataStream(stream);
333 return getBytesPerRow(headerChunk.getWidth()); 348 return getBytesPerRow(headerChunk.getWidth());
334 } 349 }
335 /** 350 /**
336 * Answer the number of bytes needed to represent a pixel. 351 * Answer the number of bytes needed to represent a pixel.
337 * This value depends on the image's color type and bit 352 * This value depends on the image's color type and bit
338 * depth. 353 * depth.
339 * Note that this method rounds up if an image's pixel size 354 * Note that this method rounds up if an image's pixel size
340 * isn't byte-aligned. 355 * isn't byte-aligned.
341 */ 356 */
342 int getBytesPerPixel() { 357 int getBytesPerPixel() {
343 int bitsPerPixel = headerChunk.getBitsPerPixel(); 358 int bitsPerPixel = headerChunk.getBitsPerPixel();
344 return (bitsPerPixel + 7) / 8; 359 return (bitsPerPixel + 7) / 8;
345 } 360 }
346 /** 361 /**
347 * Answer the number of bytes in a row of the given pixel 362 * Answer the number of bytes in a row of the given pixel
348 * width. Each row is byte-aligned, so images with bit 363 * width. Each row is byte-aligned, so images with bit
349 * depths less than a byte may have unused bits at the 364 * depths less than a byte may have unused bits at the
370 { 385 {
371 int width = headerChunk.getWidth(); 386 int width = headerChunk.getWidth();
372 int alignedBytesPerRow = getAlignedBytesPerRow(); 387 int alignedBytesPerRow = getAlignedBytesPerRow();
373 int height = headerChunk.getHeight(); 388 int height = headerChunk.getHeight();
374 if (startRow >= height || startColumn >= width) return; 389 if (startRow >= height || startColumn >= width) return;
375 390
376 int pixelsPerRow = (width - startColumn + columnInterval - 1) / columnInterval; 391 int pixelsPerRow = (width - startColumn + columnInterval - 1) / columnInterval;
377 int bytesPerRow = getBytesPerRow(pixelsPerRow); 392 int bytesPerRow = getBytesPerRow(pixelsPerRow);
378 byte[] row1 = new byte[bytesPerRow]; 393 byte[] row1 = new byte[bytesPerRow];
379 byte[] row2 = new byte[bytesPerRow]; 394 byte[] row2 = new byte[bytesPerRow];
380 byte[] currentRow = row1; 395 byte[] currentRow = row1;
381 byte[] lastRow = row2; 396 byte[] lastRow = row2;
382 for (int row = startRow; row < height; row += rowInterval) { 397 for (int row = startRow; row < height; row += rowInterval) {
383 byte filterType = cast(byte)inputStream.read(); 398 byte filterType = cast(byte)inputStream.read();
384 int read = 0; 399 int read = 0;
385 while (read !is bytesPerRow) { 400 while (read !is bytesPerRow) {
386 read += inputStream.read(currentRow, read, bytesPerRow - read); 401 read += inputStream.read(currentRow, read, bytesPerRow - read);
407 } 422 }
408 int maxShift = 8 - bitsPerPixel; 423 int maxShift = 8 - bitsPerPixel;
409 for (int byteOffset = 0; byteOffset < currentRow.length; byteOffset++) { 424 for (int byteOffset = 0; byteOffset < currentRow.length; byteOffset++) {
410 for (int bitOffset = maxShift; bitOffset >= 0; bitOffset -= bitsPerPixel) { 425 for (int bitOffset = maxShift; bitOffset >= 0; bitOffset -= bitsPerPixel) {
411 if (column < width) { 426 if (column < width) {
412 int dataOffset = rowBase + (column * bitsPerPixel / 8); 427 int dataOffset = rowBase + (column * bitsPerPixel / 8);
413 int value = (currentRow[byteOffset] >> bitOffset) & valueMask; 428 int value = (currentRow[byteOffset] >> bitOffset) & valueMask;
414 int dataShift = maxShift - (bitsPerPixel * (column % pixelsPerByte)); 429 int dataShift = maxShift - (bitsPerPixel * (column % pixelsPerByte));
415 data[dataOffset] |= value << dataShift; 430 data[dataOffset] |= value << dataShift;
416 } 431 }
417 column += columnInterval; 432 column += columnInterval;
426 } 441 }
427 /** 442 /**
428 * Read the pixel data for an interlaced image from the 443 * Read the pixel data for an interlaced image from the
429 * data stream. 444 * data stream.
430 */ 445 */
431 void readInterlacedImage(InputStream inputStream) { 446 void readInterlacedImage(InputStream inputStream) {
432 readInterlaceFrame(inputStream, 8, 8, 0, 0, 0); 447 readInterlaceFrame(inputStream, 8, 8, 0, 0, 0);
433 readInterlaceFrame(inputStream, 8, 8, 0, 4, 1); 448 readInterlaceFrame(inputStream, 8, 8, 0, 4, 1);
434 readInterlaceFrame(inputStream, 8, 4, 4, 0, 2); 449 readInterlaceFrame(inputStream, 8, 4, 4, 0, 2);
435 readInterlaceFrame(inputStream, 4, 4, 0, 2, 3); 450 readInterlaceFrame(inputStream, 4, 4, 0, 2, 3);
436 readInterlaceFrame(inputStream, 4, 2, 2, 0, 4); 451 readInterlaceFrame(inputStream, 4, 2, 2, 0, 4);
437 readInterlaceFrame(inputStream, 2, 2, 0, 1, 5); 452 readInterlaceFrame(inputStream, 2, 2, 0, 1, 5);
438 readInterlaceFrame(inputStream, 2, 1, 1, 0, 6); 453 readInterlaceFrame(inputStream, 2, 1, 1, 0, 6);
439 } 454 }
440 /** 455 /**
441 * Fire an event to let listeners know that an interlaced 456 * Fire an event to let listeners know that an interlaced
442 * frame has been loaded. 457 * frame has been loaded.
453 /** 468 /**
454 * Read the pixel data for a non-interlaced image from the 469 * Read the pixel data for a non-interlaced image from the
455 * data stream. 470 * data stream.
456 * Update the imageData to reflect the new data. 471 * Update the imageData to reflect the new data.
457 */ 472 */
458 void readNonInterlacedImage(InputStream inputStream) { 473 void readNonInterlacedImage(InputStream inputStream) {
459 int dataOffset = 0; 474 int dataOffset = 0;
460 int alignedBytesPerRow = getAlignedBytesPerRow(); 475 int alignedBytesPerRow = getAlignedBytesPerRow();
461 int bytesPerRow = getBytesPerRow(); 476 int bytesPerRow = getBytesPerRow();
462 byte[] row1 = new byte[bytesPerRow]; 477 byte[] row1 = new byte[bytesPerRow];
463 byte[] row2 = new byte[bytesPerRow]; 478 byte[] row2 = new byte[bytesPerRow];
464 byte[] currentRow = row1; 479 byte[] currentRow = row1;
465 byte[] lastRow = row2; 480 byte[] lastRow = row2;
466 int height = headerChunk.getHeight(); 481 int height = headerChunk.getHeight();
467 for (int row = 0; row < height; row++) { 482 for (int row = 0; row < height; row++) {
468 byte filterType = cast(byte)inputStream.read(); 483 byte filterType = cast(byte)inputStream.read();
469 int read = 0; 484 int read = 0;
488 * byte of the 16-bit value. 503 * byte of the 16-bit value.
489 */ 504 */
490 static void compress16BitDepthTo8BitDepth( 505 static void compress16BitDepthTo8BitDepth(
491 byte[] source, 506 byte[] source,
492 int sourceOffset, 507 int sourceOffset,
493 byte[] destination, 508 byte[] destination,
494 int destinationOffset, 509 int destinationOffset,
495 int numberOfValues) 510 int numberOfValues)
496 { 511 {
497 //double multiplier = (Compatibility.pow2(8) - 1) / (Compatibility.pow2(16) - 1); 512 //double multiplier = (Compatibility.pow2(8) - 1) / (Compatibility.pow2(16) - 1);
498 for (int i = 0; i < numberOfValues; i++) { 513 for (int i = 0; i < numberOfValues; i++) {
499 int sourceIndex = sourceOffset + (2 * i); 514 int sourceIndex = sourceOffset + (2 * i);
500 int destinationIndex = destinationOffset + i; 515 int destinationIndex = destinationOffset + i;
501 //int value = (source[sourceIndex] << 8) | source[sourceIndex + 1]; 516 //int value = (source[sourceIndex] << 8) | source[sourceIndex + 1];
502 //byte compressedValue = cast(byte)(value * multiplier); 517 //byte compressedValue = (byte)(value * multiplier);
503 byte compressedValue = source[sourceIndex]; 518 byte compressedValue = source[sourceIndex];
504 destination[destinationIndex] = compressedValue; 519 destination[destinationIndex] = compressedValue;
505 } 520 }
506 } 521 }
507 /** 522 /**
513 * The fast way to do this is just to drop the low 528 * The fast way to do this is just to drop the low
514 * byte of the 16-bit value. 529 * byte of the 16-bit value.
515 */ 530 */
516 static int compress16BitDepthTo8BitDepth(int value) { 531 static int compress16BitDepthTo8BitDepth(int value) {
517 //double multiplier = (Compatibility.pow2(8) - 1) / (Compatibility.pow2(16) - 1); 532 //double multiplier = (Compatibility.pow2(8) - 1) / (Compatibility.pow2(16) - 1);
518 //byte compressedValue = cast(byte)(value * multiplier); 533 //byte compressedValue = (byte)(value * multiplier);
519 return value >> 8; 534 return value >> 8;
520 } 535 }
521 /** 536 /**
522 * PNG supports four filtering types. These types are applied 537 * PNG supports four filtering types. These types are applied
523 * per row of image data. This method unfilters the given row 538 * per row of image data. This method unfilters the given row
536 } 551 }
537 break; 552 break;
538 case PngIhdrChunk.FILTER_UP: 553 case PngIhdrChunk.FILTER_UP:
539 for (int i = 0; i < row.length; i++) { 554 for (int i = 0; i < row.length; i++) {
540 int current = row[i] & 0xFF; 555 int current = row[i] & 0xFF;
541 int above = previousRow[i] & 0xFF; 556 int above = previousRow[i] & 0xFF;
542 row[i] = cast(byte)((current + above) & 0xFF); 557 row[i] = cast(byte)((current + above) & 0xFF);
543 } 558 }
544 break; 559 break;
545 case PngIhdrChunk.FILTER_AVERAGE: 560 case PngIhdrChunk.FILTER_AVERAGE:
546 for (int i = 0; i < row.length; i++) { 561 for (int i = 0; i < row.length; i++) {
553 case PngIhdrChunk.FILTER_PAETH: 568 case PngIhdrChunk.FILTER_PAETH:
554 for (int i = 0; i < row.length; i++) { 569 for (int i = 0; i < row.length; i++) {
555 int left = (i < byteOffset) ? 0 : row[i - byteOffset] & 0xFF; 570 int left = (i < byteOffset) ? 0 : row[i - byteOffset] & 0xFF;
556 int aboveLeft = (i < byteOffset) ? 0 : previousRow[i - byteOffset] & 0xFF; 571 int aboveLeft = (i < byteOffset) ? 0 : previousRow[i - byteOffset] & 0xFF;
557 int above = previousRow[i] & 0xFF; 572 int above = previousRow[i] & 0xFF;
558 573
559 int a = Math.abs(above - aboveLeft); 574 int a = Math.abs(above - aboveLeft);
560 int b = Math.abs(left - aboveLeft); 575 int b = Math.abs(left - aboveLeft);
561 int c = Math.abs(left - aboveLeft + above - aboveLeft); 576 int c = Math.abs(left - aboveLeft + above - aboveLeft);
562 577
563 int preductor = 0; 578 int preductor = 0;
564 if (a <= b && a <= c) { 579 if (a <= b && a <= c) {
565 preductor = left; 580 preductor = left;
566 } else if (b <= c) { 581 } else if (b <= c) {
567 preductor = above; 582 preductor = above;
568 } else { 583 } else {
569 preductor = aboveLeft; 584 preductor = aboveLeft;
570 } 585 }
571 586
572 int currentValue = row[i] & 0xFF; 587 int currentValue = row[i] & 0xFF;
573 row[i] = cast(byte) ((currentValue + preductor) & 0xFF); 588 row[i] = cast(byte) ((currentValue + preductor) & 0xFF);
574 } 589 }
575 break; 590 break;
576 } 591 default:
577 } 592 }
578 593 }
579 } 594
595 }