1 11 package org.eclipse.swt.internal.image; 12 13 14 import java.io.*; 15 import org.eclipse.swt.*; 16 import org.eclipse.swt.graphics.*; 17 import org.eclipse.swt.internal.*; 18 19 final class PNGFileFormat extends FileFormat { 20 static final int SIGNATURE_LENGTH = 8; 21 static final int PRIME = 65521; 22 PngIhdrChunk headerChunk; 23 PngPlteChunk paletteChunk; 24 ImageData imageData; 25 byte[] data; 26 byte[] alphaPalette; 27 byte headerByte1; 28 byte headerByte2; 29 int adler; 30 31 35 void readSignature() throws IOException { 36 byte[] signature = new byte[SIGNATURE_LENGTH]; 37 inputStream.read(signature); 38 } 39 42 ImageData[] loadFromByteStream() { 43 try { 44 readSignature(); 45 PngChunkReader chunkReader = new PngChunkReader(inputStream); 46 headerChunk = chunkReader.getIhdrChunk(); 47 int width = headerChunk.getWidth(), height = headerChunk.getHeight(); 48 if (width <= 0 || height <= 0) SWT.error(SWT.ERROR_INVALID_IMAGE); 49 int imageSize = getAlignedBytesPerRow() * height; 50 data = new byte[imageSize]; 51 imageData = ImageData.internal_new( 52 width, 53 height, 54 headerChunk.getSwtBitsPerPixel(), 55 new PaletteData(0, 0, 0), 56 4, 57 data, 58 0, 59 null, 60 null, 61 -1, 62 -1, 63 SWT.IMAGE_PNG, 64 0, 65 0, 66 0, 67 0); 68 69 if (headerChunk.usesDirectColor()) { 70 imageData.palette = headerChunk.getPaletteData(); 71 } 72 73 while (chunkReader.hasMoreChunks()) { 75 readNextChunk(chunkReader); 76 } 77 78 return new ImageData[] {imageData}; 79 } catch (IOException e) { 80 SWT.error(SWT.ERROR_INVALID_IMAGE); 81 return null; 82 } 83 } 84 88 void readNextChunk(PngChunkReader chunkReader) throws IOException { 89 PngChunk chunk = chunkReader.readNextChunk(); 90 switch (chunk.getChunkType()) { 91 case PngChunk.CHUNK_IEND: 92 break; 93 case PngChunk.CHUNK_PLTE: 94 if (!headerChunk.usesDirectColor()) { 95 paletteChunk = (PngPlteChunk) chunk; 96 imageData.palette = paletteChunk.getPaletteData(); 97 } 98 break; 99 case PngChunk.CHUNK_tRNS: 100 PngTrnsChunk trnsChunk = (PngTrnsChunk) chunk; 101 if (trnsChunk.getTransparencyType(headerChunk) == 102 PngTrnsChunk.TRANSPARENCY_TYPE_PIXEL) 103 { 104 imageData.transparentPixel = 105 trnsChunk.getSwtTransparentPixel(headerChunk); 106 } else { 107 alphaPalette = trnsChunk.getAlphaValues(headerChunk, paletteChunk); 108 int transparentCount = 0, transparentPixel = -1; 109 for (int i = 0; i < alphaPalette.length; i++) { 110 if ((alphaPalette[i] & 0xFF) != 255) { 111 transparentCount++; 112 transparentPixel = i; 113 } 114 } 115 if (transparentCount == 0) { 116 alphaPalette = null; 117 } else if (transparentCount == 1 && alphaPalette[transparentPixel] == 0) { 118 alphaPalette = null; 119 imageData.transparentPixel = transparentPixel; 120 } 121 } 122 break; 123 case PngChunk.CHUNK_IDAT: 124 if (chunkReader.readPixelData()) { 125 SWT.error(SWT.ERROR_INVALID_IMAGE); 130 } else { 131 PngIdatChunk dataChunk = (PngIdatChunk) chunk; 134 readPixelData(dataChunk, chunkReader); 135 } 136 break; 137 default: 138 if (chunk.isCritical()) { 139 SWT.error(SWT.ERROR_NOT_IMPLEMENTED); 141 } 142 } 143 } 144 void unloadIntoByteStream(ImageLoader loader) { 145 PngEncoder encoder = new PngEncoder(loader); 146 encoder.encode(outputStream); 147 } 148 boolean isFileFormat(LEDataInputStream stream) { 149 try { 150 byte[] signature = new byte[SIGNATURE_LENGTH]; 151 stream.read(signature); 152 stream.unread(signature); 153 if ((signature[0] & 0xFF) != 137) return false; if ((signature[1] & 0xFF) != 80) return false; if ((signature[2] & 0xFF) != 78) return false; if ((signature[3] & 0xFF) != 71) return false; if ((signature[4] & 0xFF) != 13) return false; if ((signature[5] & 0xFF) != 10) return false; if ((signature[6] & 0xFF) != 26) return false; if ((signature[7] & 0xFF) != 10) return false; return true; 162 } catch (Exception e) { 163 return false; 164 } 165 } 166 170 byte[] validateBitDepth(byte[] data) { 171 if (headerChunk.getBitDepth() > 8) { 172 byte[] result = new byte[data.length / 2]; 173 compress16BitDepthTo8BitDepth(data, 0, result, 0, result.length); 174 return result; 175 } else { 176 return data; 177 } 178 } 179 187 void setPixelData(byte[] data, ImageData imageData) { 188 switch (headerChunk.getColorType()) { 189 case PngIhdrChunk.COLOR_TYPE_GRAYSCALE_WITH_ALPHA: 190 { 191 int width = imageData.width; 192 int height = imageData.height; 193 int destBytesPerLine = imageData.bytesPerLine; 194 198 int srcBytesPerLine = getAlignedBytesPerRow(); 199 if (headerChunk.getBitDepth() > 8) srcBytesPerLine /= 2; 200 201 byte[] rgbData = new byte[destBytesPerLine * height]; 202 byte[] alphaData = new byte[width * height]; 203 for (int y = 0; y < height; y++) { 204 int srcIndex = srcBytesPerLine * y; 205 int destIndex = destBytesPerLine * y; 206 int destAlphaIndex = width * y; 207 for (int x = 0; x < width; x++) { 208 byte grey = data[srcIndex]; 209 byte alpha = data[srcIndex + 1]; 210 rgbData[destIndex + 0] = grey; 211 rgbData[destIndex + 1] = grey; 212 rgbData[destIndex + 2] = grey; 213 alphaData[destAlphaIndex] = alpha; 214 srcIndex += 2; 215 destIndex += 3; 216 destAlphaIndex++; 217 } 218 } 219 imageData.data = rgbData; 220 imageData.alphaData = alphaData; 221 break; 222 } 223 case PngIhdrChunk.COLOR_TYPE_RGB_WITH_ALPHA: 224 { 225 int width = imageData.width; 226 int height = imageData.height; 227 int destBytesPerLine = imageData.bytesPerLine; 228 int srcBytesPerLine = getAlignedBytesPerRow(); 229 233 if (headerChunk.getBitDepth() > 8) srcBytesPerLine /= 2; 234 235 byte[] rgbData = new byte[destBytesPerLine * height]; 236 byte[] alphaData = new byte[width * height]; 237 for (int y = 0; y < height; y++) { 238 int srcIndex = srcBytesPerLine * y; 239 int destIndex = destBytesPerLine * y; 240 int destAlphaIndex = width * y; 241 for (int x = 0; x < width; x++) { 242 rgbData[destIndex + 0] = data[srcIndex + 0]; 243 rgbData[destIndex + 1] = data[srcIndex + 1]; 244 rgbData[destIndex + 2] = data[srcIndex + 2]; 245 alphaData[destAlphaIndex] = data[srcIndex + 3]; 246 srcIndex += 4; 247 destIndex += 3; 248 destAlphaIndex++; 249 } 250 } 251 imageData.data = rgbData; 252 imageData.alphaData = alphaData; 253 break; 254 } 255 case PngIhdrChunk.COLOR_TYPE_RGB: 256 imageData.data = data; 257 break; 258 case PngIhdrChunk.COLOR_TYPE_PALETTE: 259 imageData.data = data; 260 if (alphaPalette != null) { 261 int size = imageData.width * imageData.height; 262 byte[] alphaData = new byte[size]; 263 byte[] pixelData = new byte[size]; 264 imageData.getPixels(0, 0, size, pixelData, 0); 265 for (int i = 0; i < pixelData.length; i++) { 266 alphaData[i] = alphaPalette[pixelData[i] & 0xFF]; 267 } 268 imageData.alphaData = alphaData; 269 } 270 break; 271 default: 272 imageData.data = data; 273 break; 274 } 275 } 276 283 void setImageDataValues(byte[] data, ImageData imageData) { 284 byte[] result = validateBitDepth(data); 285 setPixelData(result, imageData); 286 } 287 291 void readPixelData(PngIdatChunk chunk, PngChunkReader chunkReader) throws IOException { 292 InputStream stream = new PngInputStream(chunk, chunkReader); 293 boolean use3_2 = System.getProperty("org.eclipse.swt.internal.image.PNGFileFormat_3.2") != null; 295 InputStream inflaterStream = use3_2 ? null : Compatibility.newInflaterInputStream(stream); 296 if (inflaterStream != null) { 297 stream = new BufferedInputStream(inflaterStream); 298 } else { 299 stream = new PngDecodingDataStream(stream); 300 } 301 int interlaceMethod = headerChunk.getInterlaceMethod(); 302 if (interlaceMethod == PngIhdrChunk.INTERLACE_METHOD_NONE) { 303 readNonInterlacedImage(stream); 304 } else { 305 readInterlacedImage(stream); 306 } 307 312 while (stream.available() > 0) stream.read(); 313 stream.close(); 314 } 315 318 int getAlignedBytesPerRow() { 319 return ((getBytesPerRow(headerChunk.getWidth()) + 3) / 4) * 4; 320 } 321 327 int getBytesPerRow() { 328 return getBytesPerRow(headerChunk.getWidth()); 329 } 330 337 int getBytesPerPixel() { 338 int bitsPerPixel = headerChunk.getBitsPerPixel(); 339 return (bitsPerPixel + 7) / 8; 340 } 341 347 int getBytesPerRow(int rowWidthInPixels) { 348 int bitsPerPixel = headerChunk.getBitsPerPixel(); 349 int bitsPerRow = bitsPerPixel * rowWidthInPixels; 350 int bitsPerByte = 8; 351 return (bitsPerRow + (bitsPerByte - 1)) / bitsPerByte; 352 } 353 358 void readInterlaceFrame( 359 InputStream inputStream, 360 int rowInterval, 361 int columnInterval, 362 int startRow, 363 int startColumn, 364 int frameCount) throws IOException 365 { 366 int width = headerChunk.getWidth(); 367 int alignedBytesPerRow = getAlignedBytesPerRow(); 368 int height = headerChunk.getHeight(); 369 if (startRow >= height || startColumn >= width) return; 370 371 int pixelsPerRow = (width - startColumn + columnInterval - 1) / columnInterval; 372 int bytesPerRow = getBytesPerRow(pixelsPerRow); 373 byte[] row1 = new byte[bytesPerRow]; 374 byte[] row2 = new byte[bytesPerRow]; 375 byte[] currentRow = row1; 376 byte[] lastRow = row2; 377 for (int row = startRow; row < height; row += rowInterval) { 378 byte filterType = (byte)inputStream.read(); 379 int read = 0; 380 while (read != bytesPerRow) { 381 read += inputStream.read(currentRow, read, bytesPerRow - read); 382 } 383 filterRow(currentRow, lastRow, filterType); 384 if (headerChunk.getBitDepth() >= 8) { 385 int bytesPerPixel = getBytesPerPixel(); 386 int dataOffset = (row * alignedBytesPerRow) + (startColumn * bytesPerPixel); 387 for (int rowOffset = 0; rowOffset < currentRow.length; rowOffset += bytesPerPixel) { 388 for (int byteOffset = 0; byteOffset < bytesPerPixel; byteOffset++) { 389 data[dataOffset + byteOffset] = currentRow[rowOffset + byteOffset]; 390 } 391 dataOffset += (columnInterval * bytesPerPixel); 392 } 393 } else { 394 int bitsPerPixel = headerChunk.getBitDepth(); 395 int pixelsPerByte = 8 / bitsPerPixel; 396 int column = startColumn; 397 int rowBase = row * alignedBytesPerRow; 398 int valueMask = 0; 399 for (int i = 0; i < bitsPerPixel; i++) { 400 valueMask <<= 1; 401 valueMask |= 1; 402 } 403 int maxShift = 8 - bitsPerPixel; 404 for (int byteOffset = 0; byteOffset < currentRow.length; byteOffset++) { 405 for (int bitOffset = maxShift; bitOffset >= 0; bitOffset -= bitsPerPixel) { 406 if (column < width) { 407 int dataOffset = rowBase + (column * bitsPerPixel / 8); 408 int value = (currentRow[byteOffset] >> bitOffset) & valueMask; 409 int dataShift = maxShift - (bitsPerPixel * (column % pixelsPerByte)); 410 data[dataOffset] |= value << dataShift; 411 } 412 column += columnInterval; 413 } 414 } 415 } 416 currentRow = (currentRow == row1) ? row2 : row1; 417 lastRow = (lastRow == row1) ? row2 : row1; 418 } 419 setImageDataValues(data, imageData); 420 fireInterlacedFrameEvent(frameCount); 421 } 422 426 void readInterlacedImage(InputStream inputStream) throws IOException { 427 readInterlaceFrame(inputStream, 8, 8, 0, 0, 0); 428 readInterlaceFrame(inputStream, 8, 8, 0, 4, 1); 429 readInterlaceFrame(inputStream, 8, 4, 4, 0, 2); 430 readInterlaceFrame(inputStream, 4, 4, 0, 2, 3); 431 readInterlaceFrame(inputStream, 4, 2, 2, 0, 4); 432 readInterlaceFrame(inputStream, 2, 2, 0, 1, 5); 433 readInterlaceFrame(inputStream, 2, 1, 1, 0, 6); 434 } 435 441 void fireInterlacedFrameEvent(int frameCount) { 442 if (loader.hasListeners()) { 443 ImageData image = (ImageData) imageData.clone(); 444 boolean finalFrame = frameCount == 6; 445 loader.notifyListeners(new ImageLoaderEvent(loader, image, frameCount, finalFrame)); 446 } 447 } 448 453 void readNonInterlacedImage(InputStream inputStream) throws IOException { 454 int dataOffset = 0; 455 int alignedBytesPerRow = getAlignedBytesPerRow(); 456 int bytesPerRow = getBytesPerRow(); 457 byte[] row1 = new byte[bytesPerRow]; 458 byte[] row2 = new byte[bytesPerRow]; 459 byte[] currentRow = row1; 460 byte[] lastRow = row2; 461 int height = headerChunk.getHeight(); 462 for (int row = 0; row < height; row++) { 463 byte filterType = (byte)inputStream.read(); 464 int read = 0; 465 while (read != bytesPerRow) { 466 read += inputStream.read(currentRow, read, bytesPerRow - read); 467 } 468 filterRow(currentRow, lastRow, filterType); 469 System.arraycopy(currentRow, 0, data, dataOffset, bytesPerRow); 470 dataOffset += alignedBytesPerRow; 471 currentRow = (currentRow == row1) ? row2 : row1; 472 lastRow = (lastRow == row1) ? row2 : row1; 473 } 474 setImageDataValues(data, imageData); 475 } 476 485 static void compress16BitDepthTo8BitDepth( 486 byte[] source, 487 int sourceOffset, 488 byte[] destination, 489 int destinationOffset, 490 int numberOfValues) 491 { 492 for (int i = 0; i < numberOfValues; i++) { 494 int sourceIndex = sourceOffset + (2 * i); 495 int destinationIndex = destinationOffset + i; 496 byte compressedValue = source[sourceIndex]; 499 destination[destinationIndex] = compressedValue; 500 } 501 } 502 511 static int compress16BitDepthTo8BitDepth(int value) { 512 return value >> 8; 515 } 516 521 void filterRow(byte[] row, byte[] previousRow, int filterType) { 522 int byteOffset = headerChunk.getFilterByteOffset(); 523 switch (filterType) { 524 case PngIhdrChunk.FILTER_NONE: 525 break; 526 case PngIhdrChunk.FILTER_SUB: 527 for (int i = byteOffset; i < row.length; i++) { 528 int current = row[i] & 0xFF; 529 int left = row[i - byteOffset] & 0xFF; 530 row[i] = (byte)((current + left) & 0xFF); 531 } 532 break; 533 case PngIhdrChunk.FILTER_UP: 534 for (int i = 0; i < row.length; i++) { 535 int current = row[i] & 0xFF; 536 int above = previousRow[i] & 0xFF; 537 row[i] = (byte)((current + above) & 0xFF); 538 } 539 break; 540 case PngIhdrChunk.FILTER_AVERAGE: 541 for (int i = 0; i < row.length; i++) { 542 int left = (i < byteOffset) ? 0 : row[i - byteOffset] & 0xFF; 543 int above = previousRow[i] & 0xFF; 544 int current = row[i] & 0xFF; 545 row[i] = (byte)((current + ((left + above) / 2)) & 0xFF); 546 } 547 break; 548 case PngIhdrChunk.FILTER_PAETH: 549 for (int i = 0; i < row.length; i++) { 550 int left = (i < byteOffset) ? 0 : row[i - byteOffset] & 0xFF; 551 int aboveLeft = (i < byteOffset) ? 0 : previousRow[i - byteOffset] & 0xFF; 552 int above = previousRow[i] & 0xFF; 553 554 int a = Math.abs(above - aboveLeft); 555 int b = Math.abs(left - aboveLeft); 556 int c = Math.abs(left - aboveLeft + above - aboveLeft); 557 558 int preductor = 0; 559 if (a <= b && a <= c) { 560 preductor = left; 561 } else if (b <= c) { 562 preductor = above; 563 } else { 564 preductor = aboveLeft; 565 } 566 567 int currentValue = row[i] & 0xFF; 568 row[i] = (byte) ((currentValue + preductor) & 0xFF); 569 } 570 break; 571 } 572 } 573 574 } | Popular Tags |