private static IEnumerable <Face> ParseFaces(byte[] bytes, int firstRecordOffset, int posOffset, int recordLength) { if (bytes == null) { yield break; } var reader = new ByteArrayReader(bytes, isMotorolaByteOrder: false); int faceCount = reader.GetUInt16(0); if (faceCount == 0 || bytes.Length < firstRecordOffset + faceCount * recordLength) { yield break; } posOffset += firstRecordOffset; for (int i = 0, recordOffset = firstRecordOffset; i < faceCount; i++, recordOffset += recordLength, posOffset += recordLength) { yield return(new Face( x: reader.GetUInt16(posOffset), y: reader.GetUInt16(posOffset + 2), width: reader.GetUInt16(posOffset + 4), height: reader.GetUInt16(posOffset + 6), name: recordLength == 44 ? reader.GetString(recordOffset, 20, Encoding.UTF8).Trim(' ', '\0') : null, age: recordLength == 44 ? Age.FromPanasonicString(reader.GetString(recordOffset + 28, 20, Encoding.UTF8).Trim(' ', '\0')) : null)); } }
public virtual Face[] GetRecognizedFaces() { sbyte[] bytes = GetByteArray(TagFaceRecognitionInfo); if (bytes == null) { return(null); } RandomAccessReader reader = new ByteArrayReader(bytes); reader.SetMotorolaByteOrder(false); try { int faceCount = reader.GetUInt16(0); if (faceCount == 0) { return(null); } Face[] faces = new Face[faceCount]; for (int i = 0; i < faceCount; i++) { int offset = 4 + i * 44; string name = Sharpen.Extensions.Trim(reader.GetString(offset, 20, "ASCII")); string age = Sharpen.Extensions.Trim(reader.GetString(offset + 28, 20, "ASCII")); faces[i] = new Face(reader.GetUInt16(offset + 20), reader.GetUInt16(offset + 22), reader.GetUInt16(offset + 24), reader.GetUInt16(offset + 26), name, Age.FromPanasonicString(age)); } return(faces); } catch (IOException) { return(null); } }
public virtual Face[] GetDetectedFaces() { sbyte[] bytes = GetByteArray(TagFaceDetectionInfo); if (bytes == null) { return(null); } RandomAccessReader reader = new ByteArrayReader(bytes); reader.SetMotorolaByteOrder(false); try { int faceCount = reader.GetUInt16(0); if (faceCount == 0) { return(null); } Face[] faces = new Face[faceCount]; for (int i = 0; i < faceCount; i++) { int offset = 2 + i * 8; faces[i] = new Face(reader.GetUInt16(offset), reader.GetUInt16(offset + 2), reader.GetUInt16(offset + 4), reader.GetUInt16(offset + 6), null, null); } return(faces); } catch (IOException) { return(null); } }
private string GetTransformDescription(int tag) { var values = Directory.GetByteArray(tag); if (values == null) { return(null); } IndexedReader reader = new ByteArrayReader(values); try { int val1 = reader.GetUInt16(0); int val2 = reader.GetUInt16(2); if (val1 == -1 && val2 == 1) { return("Slim Low"); } if (val1 == -3 && val2 == 2) { return("Slim High"); } if (val1 == 0 && val2 == 0) { return("Off"); } if (val1 == 1 && val2 == 1) { return("Stretch Low"); } if (val1 == 3 && val2 == 2) { return("Stretch High"); } return("Unknown (" + val1 + " " + val2 + ")"); } catch (IOException) { return(null); } }
private string GetTagDataString(int tagType) { try { sbyte[] bytes = _directory.GetByteArray(tagType); if (bytes == null) { return(_directory.GetString(tagType)); } RandomAccessReader reader = new ByteArrayReader(bytes); int iccTagType = reader.GetInt32(0); switch (iccTagType) { case IccTagTypeText: { try { return(Sharpen.Runtime.GetStringForBytes(bytes, 8, bytes.Length - 8 - 1, "ASCII")); } catch (UnsupportedEncodingException) { return(Sharpen.Runtime.GetStringForBytes(bytes, 8, bytes.Length - 8 - 1)); } goto case IccTagTypeDesc; } case IccTagTypeDesc: { int stringLength = reader.GetInt32(8); return(Sharpen.Runtime.GetStringForBytes(bytes, 12, stringLength - 1)); } case IccTagTypeSig: { return(IccReader.GetStringFromInt32(reader.GetInt32(8))); } case IccTagTypeMeas: { int observerType = reader.GetInt32(8); float x = reader.GetS15Fixed16(12); float y = reader.GetS15Fixed16(16); float z = reader.GetS15Fixed16(20); int geometryType = reader.GetInt32(24); float flare = reader.GetS15Fixed16(28); int illuminantType = reader.GetInt32(32); string observerString; switch (observerType) { case 0: { observerString = "Unknown"; break; } case 1: { observerString = "1931 2°"; break; } case 2: { observerString = "1964 10°"; break; } default: { observerString = Sharpen.Extensions.StringFormat("Unknown %d", observerType); break; } } string geometryString; switch (geometryType) { case 0: { geometryString = "Unknown"; break; } case 1: { geometryString = "0/45 or 45/0"; break; } case 2: { geometryString = "0/d or d/0"; break; } default: { geometryString = Sharpen.Extensions.StringFormat("Unknown %d", observerType); break; } } string illuminantString; switch (illuminantType) { case 0: { illuminantString = "unknown"; break; } case 1: { illuminantString = "D50"; break; } case 2: { illuminantString = "D65"; break; } case 3: { illuminantString = "D93"; break; } case 4: { illuminantString = "F2"; break; } case 5: { illuminantString = "D55"; break; } case 6: { illuminantString = "A"; break; } case 7: { illuminantString = "Equi-Power (E)"; break; } case 8: { illuminantString = "F8"; break; } default: { illuminantString = Sharpen.Extensions.StringFormat("Unknown %d", illuminantType); break; } } return(Sharpen.Extensions.StringFormat("%s Observer, Backing (%s, %s, %s), Geometry %s, Flare %d%%, Illuminant %s", observerString, x, y, z, geometryString, (long)System.Math.Round(flare * 100), illuminantString)); } case IccTagTypeXyzArray: { StringBuilder res = new StringBuilder(); int count = (bytes.Length - 8) / 12; for (int i = 0; i < count; i++) { float x = reader.GetS15Fixed16(8 + i * 12); float y = reader.GetS15Fixed16(8 + i * 12 + 4); float z = reader.GetS15Fixed16(8 + i * 12 + 8); if (i > 0) { res.Append(", "); } res.Append("(").Append(x).Append(", ").Append(y).Append(", ").Append(z).Append(")"); } return(Sharpen.Extensions.ConvertToString(res)); } case IccTagTypeMluc: { int int1 = reader.GetInt32(8); StringBuilder res = new StringBuilder(); res.Append(int1); //int int2 = reader.getInt32(12); //System.err.format("int1: %d, int2: %d\n", int1, int2); for (int i = 0; i < int1; i++) { string str = IccReader.GetStringFromInt32(reader.GetInt32(16 + i * 12)); int len = reader.GetInt32(16 + i * 12 + 4); int ofs = reader.GetInt32(16 + i * 12 + 8); string name; try { name = Sharpen.Runtime.GetStringForBytes(bytes, ofs, len, "UTF-16BE"); } catch (UnsupportedEncodingException) { name = Sharpen.Runtime.GetStringForBytes(bytes, ofs, len); } res.Append(" ").Append(str).Append("(").Append(name).Append(")"); } //System.err.format("% 3d: %s, len: %d, ofs: %d, \"%s\"\n", i, str, len,ofs,name); return(Sharpen.Extensions.ConvertToString(res)); } case IccTagTypeCurv: { int num = reader.GetInt32(8); StringBuilder res = new StringBuilder(); for (int i = 0; i < num; i++) { if (i != 0) { res.Append(", "); } res.Append(FormatDoubleAsString(((float)reader.GetUInt16(12 + i * 2)) / 65535.0, 7, false)); } //res+=String.format("%1.7g",Math.round(((float)iccReader.getInt16(b,12+i*2))/0.065535)/1E7); return(Sharpen.Extensions.ConvertToString(res)); } default: { return(Sharpen.Extensions.StringFormat("%s(0x%08X): %d bytes", IccReader.GetStringFromInt32(iccTagType), iccTagType, bytes.Length)); } } } catch (IOException) { // TODO decode these values during IccReader.extract so we can report any errors at that time // It is convention to return null if a description cannot be formulated. // If an error is to be reported, it should be done during the extraction process. return(null); } }
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; } } }
private string GetTagDataString(int tagType) { try { var bytes = Directory.GetByteArray(tagType); if (bytes == null) { return(Directory.GetString(tagType)); } var reader = new ByteArrayReader(bytes); var iccTagType = (IccTagType)reader.GetInt32(0); switch (iccTagType) { case IccTagType.Text: { #if !NETSTANDARD1_3 try { return(Encoding.ASCII.GetString(bytes, 8, bytes.Length - 8 - 1)); } catch #endif { return(Encoding.UTF8.GetString(bytes, 8, bytes.Length - 8 - 1)); } } case IccTagType.Desc: { var stringLength = reader.GetInt32(8); return(Encoding.UTF8.GetString(bytes, 12, stringLength - 1)); } case IccTagType.Sig: { return(IccReader.GetStringFromUInt32(reader.GetUInt32(8))); } case IccTagType.Meas: { var observerType = reader.GetInt32(8); var x = reader.GetS15Fixed16(12); var y = reader.GetS15Fixed16(16); var z = reader.GetS15Fixed16(20); var geometryType = reader.GetInt32(24); var flare = reader.GetS15Fixed16(28); var illuminantType = reader.GetInt32(32); string observerString; switch (observerType) { case 0: observerString = "Unknown"; break; case 1: observerString = "1931 2\u00b0"; break; case 2: observerString = "1964 10\u00b0"; break; default: observerString = $"Unknown ({observerType})"; break; } string geometryString; switch (geometryType) { case 0: geometryString = "Unknown"; break; case 1: geometryString = "0/45 or 45/0"; break; case 2: geometryString = "0/d or d/0"; break; default: geometryString = $"Unknown ({observerType})"; break; } string illuminantString; switch (illuminantType) { case 0: illuminantString = "unknown"; break; case 1: illuminantString = "D50"; break; case 2: illuminantString = "D65"; break; case 3: illuminantString = "D93"; break; case 4: illuminantString = "F2"; break; case 5: illuminantString = "D55"; break; case 6: illuminantString = "A"; break; case 7: illuminantString = "Equi-Power (E)"; break; case 8: illuminantString = "F8"; break; default: illuminantString = $"Unknown ({illuminantType})"; break; } return($"{observerString} Observer, Backing ({x:0.###}, {y:0.###}, {z:0.###}), Geometry {geometryString}, Flare {(long)Math.Round(flare*100)}%, Illuminant {illuminantString}"); } case IccTagType.XyzArray: { var res = new StringBuilder(); var count = (bytes.Length - 8) / 12; for (var i = 0; i < count; i++) { var x = reader.GetS15Fixed16(8 + i * 12); var y = reader.GetS15Fixed16(8 + i * 12 + 4); var z = reader.GetS15Fixed16(8 + i * 12 + 8); if (i > 0) { res.Append(", "); } res.AppendFormat("({0:0.####}, {1:0.####}, {2:0.####})", x, y, z); } return(res.ToString()); } case IccTagType.Mluc: { var int1 = reader.GetInt32(8); var res = new StringBuilder(); res.Append(int1); for (var i = 0; i < int1; i++) { var str = IccReader.GetStringFromUInt32(reader.GetUInt32(16 + i * 12)); var len = reader.GetInt32(16 + i * 12 + 4); var ofs = reader.GetInt32(16 + i * 12 + 8); string name; try { name = Encoding.BigEndianUnicode.GetString(bytes, ofs, len); } catch { name = Encoding.UTF8.GetString(bytes, ofs, len); } res.Append(" ").Append(str).Append("(").Append(name).Append(")"); } return(res.ToString()); } case IccTagType.Curv: { var num = reader.GetInt32(8); var res = new StringBuilder(); for (var i = 0; i < num; i++) { if (i != 0) { res.Append(", "); } res.Append(FormatDoubleAsString(reader.GetUInt16(12 + i * 2) / 65535.0, 7, false)); } return(res.ToString()); } default: { return($"{IccReader.GetStringFromUInt32(unchecked((uint)iccTagType))} (0x{(int)iccTagType:X8}): {bytes.Length} bytes"); } } } catch (IOException) { // TODO decode these values during IccReader.extract so we can report any errors at that time // It is convention to return null if a description cannot be formulated. // If an error is to be reported, it should be done during the extraction process. return(null); } }
public string GetJpegQualityString() { try { var b = Directory.GetByteArray(PhotoshopDirectory.TagJpegQuality); if (b == null) { return(Directory.GetString(PhotoshopDirectory.TagJpegQuality)); } var reader = new ByteArrayReader(b); int q = reader.GetUInt16(0); int f = reader.GetUInt16(2); int s = reader.GetUInt16(4); var q1 = q <= 0xFFFF && q >= 0xFFFD ? q - 0xFFFC : q <= 8 ? q + 4 : q; string quality; switch (q) { case 0xFFFD: case 0xFFFE: case 0xFFFF: case 0: quality = "Low"; break; case 1: case 2: case 3: quality = "Medium"; break; case 4: case 5: quality = "High"; break; case 6: case 7: case 8: quality = "Maximum"; break; default: quality = "Unknown"; break; } string format; switch (f) { case 0x0000: format = "Standard"; break; case 0x0001: format = "Optimised"; break; case 0x0101: format = "Progressive"; break; default: format = $"Unknown (0x{f:X4})"; break; } var scans = s >= 1 && s <= 3 ? (s + 2).ToString() : $"Unknown (0x{s:X4})"; return($"{q1} ({quality}), {format} format, {scans} scans"); } catch { return(null); } }
public virtual string GetJpegQualityString() { try { sbyte[] b = _directory.GetByteArray(PhotoshopDirectory.TagJpegQuality); if (b == null) { return(_directory.GetString(PhotoshopDirectory.TagJpegQuality)); } RandomAccessReader reader = new ByteArrayReader(b); int q = reader.GetUInt16(0); // & 0xFFFF; int f = reader.GetUInt16(2); // & 0xFFFF; int s = reader.GetUInt16(4); int q1; if (q <= unchecked ((int)(0xFFFF)) && q >= unchecked ((int)(0xFFFD))) { q1 = q - unchecked ((int)(0xFFFC)); } else { if (q <= 8) { q1 = q + 4; } else { q1 = q; } } string quality; switch (q) { case unchecked ((int)(0xFFFD)): case unchecked ((int)(0xFFFE)): case unchecked ((int)(0xFFFF)): case 0: { quality = "Low"; break; } case 1: case 2: case 3: { quality = "Medium"; break; } case 4: case 5: { quality = "High"; break; } case 6: case 7: case 8: { quality = "Maximum"; break; } default: { quality = "Unknown"; break; } } string format; switch (f) { case unchecked ((int)(0x0000)): { format = "Standard"; break; } case unchecked ((int)(0x0001)): { format = "Optimised"; break; } case unchecked ((int)(0x0101)): { format = "Progressive "; break; } default: { format = Sharpen.Extensions.StringFormat("Unknown 0x%04X", f); break; } } string scans = s >= 1 && s <= 3 ? Sharpen.Extensions.StringFormat("%d", s + 2) : Sharpen.Extensions.StringFormat("Unknown 0x%04X", s); return(Sharpen.Extensions.StringFormat("%d (%s), %s format, %s scans", q1, quality, format, scans)); } catch (IOException) { return(null); } }
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; } } }