internal static void ReadChunk(Stream PngImageData, TPdfPngData OutData) { if (PngImageData.Length - PngImageData.Position < 3 * 4) { PngImageData.Position = PngImageData.Length; return; } UInt32 Len = GetUInt32(PngImageData); UInt32 ChunkType = GetUInt32(PngImageData); switch ((TChunkType)ChunkType) { case TChunkType.IHDR: ReadIHDR(Len, PngImageData, OutData); break; case TChunkType.IDAT: ReadIDAT(Len, PngImageData, OutData); break; case TChunkType.PLTE: ReadPLTE(Len, PngImageData, OutData); break; case TChunkType.tRNS: ReadtRNS(Len, PngImageData, OutData); break; default: PngImageData.Position += Len; break; //ignore chunk } /*UInt32 CRC = */ GetUInt32(PngImageData); }
internal TPngInformation(TPdfPngData Data) { FHeight = Data.Height; FWidth = Data.Width; FBitDepth = Data.BitDepth; FColorType = Data.ColorType; FCompressionMethod = Data.CompressionMethod; FFilterMethod = Data.FilterMethod; FInterlaceMethod = Data.InterlaceMethod; }
internal static void ReadIHDR(UInt32 Len, Stream PngImageData, TPdfPngData OutData) { OutData.Width = GetUInt32(PngImageData); OutData.Height = GetUInt32(PngImageData); OutData.BitDepth = (byte)PngImageData.ReadByte(); OutData.ColorType = (byte)PngImageData.ReadByte(); OutData.CompressionMethod = (byte)PngImageData.ReadByte(); OutData.FilterMethod = (byte)PngImageData.ReadByte(); OutData.InterlaceMethod = (byte)PngImageData.ReadByte(); }
/// <summary> /// Returns the basic information on a png file. Null if the file is not PNG. /// </summary> /// <param name="PngImageData">Stream with the image data.</param> /// <returns>Null if the image is invalid, or the image properties otherwise.</returns> public static TPngInformation GetPngInfo(Stream PngImageData) { if (!CheckHeaders(PngImageData)) { return(null); } TPdfPngData OutData = new TPdfPngData(null); ReadChunk(PngImageData, OutData); return(new TPngInformation(OutData)); }
internal static void ReadIDAT(UInt32 Len, Stream PngImageData, TPdfPngData OutData) { const int size = 4096; byte[] bytes = new byte[size]; int Remaining = (int)Len; int numBytes; while (Remaining > 0) { numBytes = PngImageData.Read(bytes, 0, Math.Min(size, Remaining)); OutData.Data.Write(bytes, 0, numBytes); Remaining -= numBytes; } }
internal static bool IsOkPng(Stream PngImageData) { if (!CheckHeaders(PngImageData)) { return(false); } TPdfPngData OutData = new TPdfPngData(null); ReadChunk(PngImageData, OutData); if (OutData.InterlaceMethod != 0 || OutData.BitDepth > 8) { return(false); } return(true); }
internal static void ProcessPng(Stream PngImageData, TPdfPngData OutData) { if (!CheckHeaders(PngImageData)) { PdfMessages.ThrowException(PdfErr.ErrInvalidPngImage); } while (PngImageData.Position < PngImageData.Length) { ReadChunk(PngImageData, OutData); } if (OutData.ColorType == 4) { SuppressAlpha(OutData, 1); } if (OutData.ColorType == 6) { SuppressAlpha(OutData, 3); } }
private void ReadPng(Stream Ms) { using (MemoryStream OutMs = new MemoryStream()) { TPdfPngData ImgParsedData = new TPdfPngData(OutMs); TPdfPng.ProcessPng(Ms, ImgParsedData); FImage = OutMs.ToArray(); FImageWidth = (int)ImgParsedData.Width; FImageHeight = (int)ImgParsedData.Height; FBitsPerComponent = ImgParsedData.BitDepth; if (FBitsPerComponent == 16) { PdfMessages.ThrowException(PdfErr.ErrInvalidPngImage); } int Colors = 1; switch (ImgParsedData.ColorType) { case 0: //GrayScale FColorSpace = TPdfTokens.GetString(TPdfToken.DeviceGrayName); if (ImgParsedData.tRNS != null && ImgParsedData.tRNS.Length >= 2) { FMask = TPdfTokens.GetString(TPdfToken.OpenArray) + String.Format(CultureInfo.InvariantCulture, "{0} {0}", (ImgParsedData.tRNS[0] << 8) + ImgParsedData.tRNS[1]) + TPdfTokens.GetString(TPdfToken.CloseArray); } break; case 2: //TrueColor FColorSpace = TPdfTokens.GetString(TPdfToken.DeviceRGBName); Colors = 3; if (ImgParsedData.tRNS != null && ImgParsedData.tRNS.Length >= 6) { FMask = TPdfTokens.GetString(TPdfToken.OpenArray) + String.Format(CultureInfo.InvariantCulture, "{0} {0} {1} {1} {2} {2}", (ImgParsedData.tRNS[0] << 8) + ImgParsedData.tRNS[1], (ImgParsedData.tRNS[2] << 8) + ImgParsedData.tRNS[3], (ImgParsedData.tRNS[4] << 8) + ImgParsedData.tRNS[5] ) + TPdfTokens.GetString(TPdfToken.CloseArray); } break; case 3: //Indexed Color FColorSpace = GetPalette(ImgParsedData.PLTE); if (ImgParsedData.tRNS != null && ImgParsedData.tRNS.Length > 0) { ImgParsedData.SMask = TPdfPng.GetIndexedSMask(FImage, FImageWidth, FImageHeight, ImgParsedData.tRNS, FBitsPerComponent); } break; case 4: //GrayScale + Alpha FColorSpace = TPdfTokens.GetString(TPdfToken.DeviceGrayName); break; case 6: //TrueColor + Alpha FColorSpace = TPdfTokens.GetString(TPdfToken.DeviceRGBName); Colors = 3; break; default: PdfMessages.ThrowException(PdfErr.ErrInvalidPngImage); break; } FSMask = ImgParsedData.SMask; OneBitMask = ImgParsedData.OneBitMask; if (ImgParsedData.InterlaceMethod != 0) { PdfMessages.ThrowException(PdfErr.ErrInvalidPngImage); } FFilterName = TPdfTokens.GetString(TPdfToken.FlateDecodeName); FDecodeParmsName = TPdfTokens.GetString(TPdfToken.StartDictionary) + String.Format(CultureInfo.InvariantCulture, TPdfTokens.GetString(TPdfToken.PredictorName) + " {0} ", 15) + String.Format(CultureInfo.InvariantCulture, TPdfTokens.GetString(TPdfToken.ColorsName) + " {0} ", Colors) + String.Format(CultureInfo.InvariantCulture, TPdfTokens.GetString(TPdfToken.BitsPerComponentName) + " {0} ", FBitsPerComponent) + String.Format(CultureInfo.InvariantCulture, TPdfTokens.GetString(TPdfToken.ColumnsName) + " {0} ", FImageWidth) + TPdfTokens.GetString(TPdfToken.EndDictionary); FDecodeParmsSMask = TPdfTokens.GetString(TPdfToken.StartDictionary) + String.Format(CultureInfo.InvariantCulture, TPdfTokens.GetString(TPdfToken.PredictorName) + " {0} ", 15) + String.Format(CultureInfo.InvariantCulture, TPdfTokens.GetString(TPdfToken.ColorsName) + " {0} ", 1) + String.Format(CultureInfo.InvariantCulture, TPdfTokens.GetString(TPdfToken.BitsPerComponentName) + " {0} ", 8) + String.Format(CultureInfo.InvariantCulture, TPdfTokens.GetString(TPdfToken.ColumnsName) + " {0} ", FImageWidth) + TPdfTokens.GetString(TPdfToken.EndDictionary); } }
//We can't crop the image here, without changing the filters. private static void SuppressAlpha(TPdfPngData OutData, byte IntBytes) { using (TCompressor Cmp = new TCompressor()) { using (TCompressor Cmp2 = new TCompressor()) { using (TCompressor Cmp3 = new TCompressor()) { const int BuffLen = 4096; bool MaskEmpty = true; bool IsOneBitMask = true; int h = (int)OutData.Height; int w = (int)OutData.Width; #if (FRAMEWORK20 || ICSHARP) && !COMPACTFRAMEWORK byte[] NewData = new byte[BuffLen + IntBytes]; byte[] SMask = new byte[BuffLen + 1]; byte[] OneBitMask = new byte[BuffLen + 1]; #else sbyte[] NewData = new sbyte[BuffLen + IntBytes]; sbyte[] SMask = new sbyte[BuffLen + 1]; sbyte[] OneBitMask = new sbyte[BuffLen + 1]; #endif using (MemoryStream InflatedStream = new MemoryStream()) { unchecked { int NewDataPos = 0; int SMaskPos = 0; int OneBitMaskPos = 0; Cmp.Inflate(OutData.Data.ToArray(), 0, InflatedStream); InflatedStream.Position = 0; Cmp.BeginDeflate(); Cmp2.BeginDeflate(); Cmp3.BeginDeflate(); OutData.Data.SetLength(0); Stream OutStream = OutData.Data; using (MemoryStream SMaskStream = new MemoryStream()) { using (MemoryStream OneBitMaskStream = new MemoryStream()) { int OneBitMaskInnerPos = 128; for (int r = 0; r < h; r++) { byte LastSMask = 0; #if (FRAMEWORK20 || ICSHARP) && !COMPACTFRAMEWORK byte RowFilter = (byte)InflatedStream.ReadByte(); #else sbyte RowFilter = (sbyte)InflatedStream.ReadByte(); #endif #region inlined //for speed on debug mode if (NewDataPos >= BuffLen) { Cmp.IncDeflate(NewData, 0, NewDataPos, OutStream); NewDataPos = 0; } if (SMaskPos >= BuffLen) { Cmp2.IncDeflate(SMask, 0, SMaskPos, SMaskStream); SMaskPos = 0; } if (OneBitMaskPos >= BuffLen) { Cmp3.IncDeflate(OneBitMask, 0, OneBitMaskPos, OneBitMaskStream); OneBitMaskPos = 0; OneBitMask[OneBitMaskPos] = 0; OneBitMaskInnerPos = 128; } #endregion NewData[NewDataPos++] = RowFilter; SMask[SMaskPos++] = RowFilter; if (IsOneBitMask) { if (OneBitMaskInnerPos < 128) { OneBitMaskPos++; //finish row. OneBitMask[OneBitMaskPos] = 0; OneBitMaskInnerPos = 128; } } for (int c = 0; c < w; c++) { #region inlined //for speed on debug mode if (NewDataPos >= BuffLen) { Cmp.IncDeflate(NewData, 0, NewDataPos, OutStream); NewDataPos = 0; } if (SMaskPos >= BuffLen) { Cmp2.IncDeflate(SMask, 0, SMaskPos, SMaskStream); SMaskPos = 0; } if (OneBitMaskPos >= BuffLen) { Cmp3.IncDeflate(OneBitMask, 0, OneBitMaskPos, OneBitMaskStream); OneBitMaskPos = 0; OneBitMask[OneBitMaskPos] = 0; OneBitMaskInnerPos = 128; } #endregion for (int b = 0; b < IntBytes; b++) #if (FRAMEWORK20 || ICSHARP) && !COMPACTFRAMEWORK { NewData[NewDataPos++] = (byte)InflatedStream.ReadByte(); } byte SMaskData = (byte)InflatedStream.ReadByte(); SMask[SMaskPos++] = SMaskData; #else { NewData[NewDataPos++] = (sbyte)InflatedStream.ReadByte(); } byte SMaskData = (byte)InflatedStream.ReadByte(); SMask[SMaskPos++] = (sbyte)SMaskData; #endif if (MaskEmpty && SMaskData != 0xFF) { MaskEmpty = false; } if (IsOneBitMask) { //SMaskData might have been flushed, and so contain invalid data. //So we need a separate LastSMask for sub filter, and the whole last scanline for the others. //As this is only an optimization (it will work the same with an SMask), we will only contemplate filters 0 and 1. if (RowFilter == 1) //sub { unchecked { SMaskData += LastSMask; } LastSMask = SMaskData; } if (RowFilter > 1 || (SMaskData != 0xFF && SMaskData != 0)) { IsOneBitMask = false; } else { #if (FRAMEWORK20 || ICSHARP) && !COMPACTFRAMEWORK OneBitMask[OneBitMaskPos] |= (byte)(~SMaskData & OneBitMaskInnerPos); #else OneBitMask[OneBitMaskPos] = (sbyte)((byte)OneBitMask[OneBitMaskPos] | (~(byte)SMaskData & OneBitMaskInnerPos)); #endif if (OneBitMaskInnerPos > 1) { OneBitMaskInnerPos >>= 1; } else { OneBitMaskPos++; OneBitMask[OneBitMaskPos] = 0; OneBitMaskInnerPos = 128; } } } } } #region inlined //for speed on debug mode if (NewDataPos > 0) { Cmp.IncDeflate(NewData, 0, NewDataPos, OutStream); NewDataPos = 0; } if (SMaskPos > 0) { Cmp2.IncDeflate(SMask, 0, SMaskPos, SMaskStream); SMaskPos = 0; } if (OneBitMaskPos > 0) { if (OneBitMaskInnerPos < 128) { OneBitMaskPos++; //finish row. } Cmp3.IncDeflate(OneBitMask, 0, OneBitMaskPos, OneBitMaskStream); OneBitMaskPos = 0; OneBitMask[OneBitMaskPos] = 0; } #endregion Cmp.EndDeflate(OutStream); Cmp2.EndDeflate(SMaskStream); Cmp3.EndDeflate(OneBitMaskStream); if (!MaskEmpty) { if (IsOneBitMask) { OutData.OneBitMask = OneBitMaskStream.ToArray(); } OutData.SMask = SMaskStream.ToArray(); } } } } } } } } }
internal static void ReadtRNS(UInt32 Len, Stream PngImageData, TPdfPngData OutData) { OutData.tRNS = new byte[Len]; Sh.Read(PngImageData, OutData.tRNS, 0, (int)Len); }