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.ASCII).Trim(' ', '\0') : null, age: recordLength == 44 ? Age.FromPanasonicString(reader.GetString(recordOffset + 28, 20, Encoding.ASCII).Trim(' ', '\0')) : 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; IndexedReader reader = new ByteArrayReader(payload); reader.IsMotorolaByteOrder = false; try { // Flags // var hasFragments = reader.getBit(0); var isAnimation = reader.GetBit(1); // var hasXmp = reader.getBit(2); // var hasExif = reader.getBit(3); var hasAlpha = reader.GetBit(4); // var hasIcc = reader.getBit(5); // Image size var widthMinusOne = reader.GetInt24(4); var heightMinusOne = reader.GetInt24(7); var directory = new WebPDirectory(); directory.Set(WebPDirectory.TagImageWidth, widthMinusOne + 1); directory.Set(WebPDirectory.TagImageHeight, heightMinusOne + 1); directory.Set(WebPDirectory.TagHasAlpha, hasAlpha); directory.Set(WebPDirectory.TagIsAnimation, isAnimation); _directories.Add(directory); } catch (IOException e) { Debug.WriteLine(e); } break; } case "VP8L": { if (payload.Length < 5) break; IndexedReader reader = new ByteArrayReader(payload); reader.IsMotorolaByteOrder = false; 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 var widthMinusOne = (b2 & 0x3F) << 8 | b1; // 14 bits for height var heightMinusOne = (b4 & 0x0F) << 10 | b3 << 2 | (b2 & 0xC0) >> 6; var directory = new WebPDirectory(); directory.Set(WebPDirectory.TagImageWidth, widthMinusOne + 1); directory.Set(WebPDirectory.TagImageHeight, heightMinusOne + 1); _directories.Add(directory); } catch (IOException e) { Debug.WriteLine(e); } break; } case "VP8 ": { if (payload.Length < 10) break; IndexedReader reader = new ByteArrayReader(payload); reader.IsMotorolaByteOrder = false; 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; var width = reader.GetUInt16(6); var height = reader.GetUInt16(8); var directory = new WebPDirectory(); directory.Set(WebPDirectory.TagImageWidth, width); directory.Set(WebPDirectory.TagImageHeight, height); _directories.Add(directory); } catch (IOException e) { Debug.WriteLine(e); } break; } } }