/// <summary> /// Decode raw CFAPattern value /// </summary> /// <remarks> /// Converted from Exiftool version 10.33 created by Phil Harvey /// http://www.sno.phy.queensu.ca/~phil/exiftool/ /// lib\Image\ExifTool\Exif.pm /// /// The value consists of: /// - Two short, being the grid width and height of the repeated pattern. /// - Next, for every pixel in that pattern, an identification code. /// </remarks> private int[] DecodeCFAPattern(int tagType) { int[] ret; var values = Directory.GetByteArray(tagType); if (values == null) { return(null); } if (values.Length < 4) { ret = new int[values.Length]; for (var i = 0; i < values.Length; i++) { ret[i] = values[i]; } return(ret); } IndexedReader reader = new ByteArrayReader(values); // first two values should be read as 16-bits (2 bytes) var item0 = reader.GetInt16(0); var item1 = reader.GetInt16(2); ret = new int[values.Length - 2]; var copyArray = false; var end = 2 + item0 * item1; if (end > values.Length) // sanity check in case of byte order problems; calculated 'end' should be <= length of the values { // try swapping byte order (I have seen this order different than in EXIF) reader = reader.WithByteOrder(!reader.IsMotorolaByteOrder); item0 = reader.GetInt16(0); item1 = reader.GetInt16(2); if (values.Length >= 2 + item0 * item1) { copyArray = true; } } else { copyArray = true; } if (copyArray) { ret[0] = item0; ret[1] = item1; for (var i = 4; i < values.Length; i++) { ret[i - 2] = reader.GetByte(i); } } return(ret); }
public void ProcessChunk(string fourCc, byte[] payload) { switch (fourCc) { case "EXIF": { // We have seen WebP images with and without the preamble here. It's likely that some software incorrectly // copied an entire JPEG segment into the WebP image. Regardless, we can handle it here. var reader = ExifReader.StartsWithJpegExifPreamble(payload) ? new ByteArrayReader(payload, ExifReader.JpegSegmentPreambleLength) : new ByteArrayReader(payload); _directories.AddRange(new ExifReader().Extract(reader)); break; } case "ICCP": { _directories.Add(new IccReader().Extract(new ByteArrayReader(payload))); break; } case "XMP ": { _directories.Add(new XmpReader().Extract(payload)); break; } case "VP8X": { if (payload.Length != 10) { break; } string?error = null; var reader = new ByteArrayReader(payload, isMotorolaByteOrder: false); var isAnimation = false; var hasAlpha = false; var widthMinusOne = -1; var heightMinusOne = -1; try { // Flags // bit 0: has fragments // bit 1: is animation // bit 2: has XMP // bit 3: has Exif // bit 4: has alpha // big 5: has ICC isAnimation = reader.GetBit(1); hasAlpha = reader.GetBit(4); // Image size widthMinusOne = reader.GetInt24(4); heightMinusOne = reader.GetInt24(7); } catch (IOException e) { error = "Exception reading WebpRiff chunk 'VP8X' : " + e.Message; } var directory = new WebPDirectory(); if (error == null) { directory.Set(WebPDirectory.TagImageWidth, widthMinusOne + 1); directory.Set(WebPDirectory.TagImageHeight, heightMinusOne + 1); directory.Set(WebPDirectory.TagHasAlpha, hasAlpha); directory.Set(WebPDirectory.TagIsAnimation, isAnimation); } else { directory.AddError(error); } _directories.Add(directory); break; } case "VP8L": { if (payload.Length < 5) { break; } var reader = new ByteArrayReader(payload, isMotorolaByteOrder: false); string?error = null; var widthMinusOne = -1; var heightMinusOne = -1; try { // https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification#2_riff_header // Expect the signature byte if (reader.GetByte(0) != 0x2F) { break; } var b1 = reader.GetByte(1); var b2 = reader.GetByte(2); var b3 = reader.GetByte(3); var b4 = reader.GetByte(4); // 14 bits for width widthMinusOne = (b2 & 0x3F) << 8 | b1; // 14 bits for height heightMinusOne = (b4 & 0x0F) << 10 | b3 << 2 | (b2 & 0xC0) >> 6; } catch (IOException e) { error = "Exception reading WebpRiff chunk 'VP8L' : " + e.Message; } var directory = new WebPDirectory(); if (error == null) { directory.Set(WebPDirectory.TagImageWidth, widthMinusOne + 1); directory.Set(WebPDirectory.TagImageHeight, heightMinusOne + 1); } else { directory.AddError(error); } _directories.Add(directory); break; } case "VP8 ": { if (payload.Length < 10) { break; } var reader = new ByteArrayReader(payload, isMotorolaByteOrder: false); string?error = null; var width = 0; var height = 0; try { // https://tools.ietf.org/html/rfc6386#section-9.1 // https://github.com/webmproject/libwebp/blob/master/src/enc/syntax.c#L115 // Expect the signature bytes if (reader.GetByte(3) != 0x9D || reader.GetByte(4) != 0x01 || reader.GetByte(5) != 0x2A) { break; } width = reader.GetUInt16(6); height = reader.GetUInt16(8); } catch (IOException e) { error = "Exception reading WebpRiff chunk 'VP8' : " + e.Message; } var directory = new WebPDirectory(); if (error == null) { directory.Set(WebPDirectory.TagImageWidth, width); directory.Set(WebPDirectory.TagImageHeight, height); } else { directory.AddError(error); } _directories.Add(directory); break; } } }
public void ProcessChunk(string fourCc, byte[] payload) { switch (fourCc) { case "EXIF": { _directories.AddRange(new ExifReader().Extract(new ByteArrayReader(payload))); break; } case "ICCP": { _directories.Add(new IccReader().Extract(new ByteArrayReader(payload))); break; } case "XMP ": { _directories.Add(new XmpReader().Extract(payload)); break; } case "VP8X": { if (payload.Length != 10) { break; } string error = null; var reader = new ByteArrayReader(payload, isMotorolaByteOrder: false); var isAnimation = false; var hasAlpha = false; var widthMinusOne = -1; var heightMinusOne = -1; try { // Flags // var hasFragments = reader.GetBit(0); isAnimation = reader.GetBit(1); // var hasXmp = reader.GetBit(2); // var hasExif = reader.GetBit(3); hasAlpha = reader.GetBit(4); // var hasIcc = reader.GetBit(5); // Image size widthMinusOne = reader.GetInt24(4); heightMinusOne = reader.GetInt24(7); } catch (IOException e) { error = "Exception reading WebpRiff chunk 'VP8X' : " + e.Message; } var directory = new WebPDirectory(); if (error == null) { directory.Set(WebPDirectory.TagImageWidth, widthMinusOne + 1); directory.Set(WebPDirectory.TagImageHeight, heightMinusOne + 1); directory.Set(WebPDirectory.TagHasAlpha, hasAlpha); directory.Set(WebPDirectory.TagIsAnimation, isAnimation); } else { directory.AddError(error); } _directories.Add(directory); break; } case "VP8L": { if (payload.Length < 5) { break; } var reader = new ByteArrayReader(payload, isMotorolaByteOrder: false); string error = null; var widthMinusOne = -1; var heightMinusOne = -1; try { // https://developers.google.com/speed/webp/docs/webp_lossless_bitstream_specification#2_riff_header // Expect the signature byte if (reader.GetByte(0) != 0x2F) { break; } var b1 = reader.GetByte(1); var b2 = reader.GetByte(2); var b3 = reader.GetByte(3); var b4 = reader.GetByte(4); // 14 bits for width widthMinusOne = (b2 & 0x3F) << 8 | b1; // 14 bits for height heightMinusOne = (b4 & 0x0F) << 10 | b3 << 2 | (b2 & 0xC0) >> 6; } catch (IOException e) { error = "Exception reading WebpRiff chunk 'VP8L' : " + e.Message; } var directory = new WebPDirectory(); if (error == null) { directory.Set(WebPDirectory.TagImageWidth, widthMinusOne + 1); directory.Set(WebPDirectory.TagImageHeight, heightMinusOne + 1); } else { directory.AddError(error); } _directories.Add(directory); break; } case "VP8 ": { if (payload.Length < 10) { break; } var reader = new ByteArrayReader(payload, isMotorolaByteOrder: false); string error = null; var width = 0; var height = 0; try { // https://tools.ietf.org/html/rfc6386#section-9.1 // https://github.com/webmproject/libwebp/blob/master/src/enc/syntax.c#L115 // Expect the signature bytes if (reader.GetByte(3) != 0x9D || reader.GetByte(4) != 0x01 || reader.GetByte(5) != 0x2A) { break; } width = reader.GetUInt16(6); height = reader.GetUInt16(8); } catch (IOException e) { error = "Exception reading WebpRiff chunk 'VP8' : " + e.Message; } var directory = new WebPDirectory(); if (error == null) { directory.Set(WebPDirectory.TagImageWidth, width); directory.Set(WebPDirectory.TagImageHeight, height); } else { directory.AddError(error); } _directories.Add(directory); break; } } }