#pragma description Tag Image File Format #pragma MIME image/tiff #pragma eval_depth 100 import std.io; import std.mem; import std.string; import std.core; char Magic[2] @ 0 [[hidden]]; match (Magic) { ("II"): std::core::set_endian(std::mem::Endian::Little); ("MM"): std::core::set_endian(std::mem::Endian::Big); (_): std::error(std::format("Unrecognized magic number: {}", Magic)); } u16 Version @ 2 [[hidden]]; u32 ValueOffsetSize; match (Version) { (42): ValueOffsetSize = 4; (43): ValueOffsetSize = 8; (_): std::error(std::format("Unrecognized version: {}", Version)); } struct Big { match (Version) { (42): T V; (43): u64 V; (_): std::error(std::format("Unrecognized version: {}", Version)); } } [[sealed, format_read("format_read_big"), format_write("format_write_big"), transform("transform_big")]]; fn format_read_big(Big v) { return std::format("{} (0x{:X})", v, v); }; fn format_write_big(str v) { return std::string::parse_int(v, 0); }; fn transform_big(Big v) { return v.V; }; struct TIFFHeader { char Magic[2]; u16 Version; if (Version > 42) { u16 OffsetSize; padding[2]; } Big Offset; }; enum TIFFFieldType : u16 { BYTE = 1, ASCII = 2, SHORT = 3, LONG = 4, RATIONAL = 5, SBYTE = 6, UNDEFINED = 7, SSHORT = 8, SLONG = 9, SRATIONAL = 10, FLOAT = 11, DOUBLE = 12, LONG8 = 16, SLONG8 = 17, }; struct TIFFRational { T Numerator; T Denominator; } [[format_read("format_read_rational")]]; fn format_read_rational(auto r) { return std::format("{}/{}", r.Numerator, r.Denominator); }; enum TIFFTag : u16 { NewSubfileType = 0x00FE, SubfileType = 0x00FF, ImageWidth = 0x0100, ImageLength = 0x0101, BitsPerSample = 0x0102, Compression = 0x0103, PhotometricInterpretation = 0x0106, Threshholding = 0x0107, CellWidth = 0x0108, CellLength = 0x0109, FillOrder = 0x010A, DocumentName = 0x010D, ImageDescription = 0x010E, Make = 0x010F, Model = 0x0110, StripOffsets = 0x0111, Orientation = 0x0112, SamplesPerPixel = 0x0115, RowsPerStrip = 0x0116, StripByteCounts = 0x0117, MinSampleValue = 0x0118, MaxSampleValue = 0x0119, XResolution = 0x011A, YResolution = 0x011B, PlanarConfiguration = 0x011C, PageName = 0x011D, XPosition = 0x011E, YPosition = 0x011F, FreeOffsets = 0x0120, FreeByteCounts = 0x0121, GrayResponseUnit = 0x0122, GrayResponseCurve = 0x0123, T4Options = 0x0124, T6Options = 0x0125, ResolutionUnit = 0x0128, PageNumber = 0x0129, TransferFunction = 0x012D, Software = 0x0131, DateTime = 0x0132, Artist = 0x013B, HostComputer = 0x013C, Predictor = 0x013D, WhitePoint = 0x013E, PrimaryChromaticities = 0x013F, ColorMap = 0x0140, HalftoneHints = 0x0141, TileWidth = 0x0142, TileLength = 0x0143, TileOffsets = 0x0144, TileByteCounts = 0x0145, InkSet = 0x014C, InkNames = 0x014D, NumberOfInks = 0x014E, DotRange = 0x0150, TargetPrinter = 0x0151, ExtraSamples = 0x0152, SampleFormat = 0x0153, SMinSampleValue = 0x0154, SMaxSampleValue = 0x0155, TransferRange = 0x0156, JPEGTables = 0x015B, JPEGProc = 0x0200, JPEGInterchangeFormat = 0x0201, JPEGInterchangeFormatLngth = 0x0202, JPEGRestartInterval = 0x0203, JPEGLosslessPredictors = 0x0205, JPEGPointTransforms = 0x0206, JPEGQTables = 0x0207, JPEGDCTables = 0x0208, JPEGACTables = 0x0209, YCbCrCoefficients = 0x0211, YCbCrSubSampling = 0x0212, YCbCrPositioning = 0x0213, ReferenceBlackWhite = 0x0214, Copyright = 0x8298, ICCProfile = 0x8773 }; struct ValueArray { if (Count > 1) { T Values[Count]; } else { T Values[Count] [[hidden, no_unique_address]]; T Value; } } [[inline]]; struct ValueOffset { u64 Size = sizeof(T) * Count; if (Size <= ValueOffsetSize) { ValueArray ValueArray; padding[ValueOffsetSize - Size]; } else { Big Offset; ValueArray ValueArray @ Offset; } } [[inline]]; struct IFDEntry { TIFFTag Tag; TIFFFieldType Type; Big Count; match (Type) { (TIFFFieldType::BYTE): ValueOffset ValueOffset; (TIFFFieldType::ASCII): ValueOffset ValueOffset; (TIFFFieldType::SHORT): ValueOffset ValueOffset; (TIFFFieldType::LONG): ValueOffset ValueOffset; (TIFFFieldType::RATIONAL): ValueOffset, Count> ValueOffset; (TIFFFieldType::SBYTE): ValueOffset ValueOffset; (TIFFFieldType::UNDEFINED): ValueOffset ValueOffset; (TIFFFieldType::SSHORT): ValueOffset ValueOffset; (TIFFFieldType::SLONG): ValueOffset ValueOffset; (TIFFFieldType::SRATIONAL): ValueOffset, Count> ValueOffset; (TIFFFieldType::FLOAT): ValueOffset ValueOffset; (TIFFFieldType::DOUBLE): ValueOffset ValueOffset; (TIFFFieldType::LONG8): ValueOffset ValueOffset; (TIFFFieldType::SLONG8): ValueOffset ValueOffset; (_): { padding[ValueOffsetSize]; std::print(std::format("TIFFFieldType {} not supported", u16(Type))); } } } [[name(std::string::replace(std::core::formatted_value(Tag), "TIFFTag::", ""))]]; fn get_field(ref auto entries, TIFFTag tag) { for (u64 i = 0, i < std::core::member_count(entries), i = i + 1) { if (entries[i].Tag == tag) { return i; } } std::error(std::format("Tag {} not found in directory", tag)); }; struct ImageData { u64 Index = std::core::array_index(); u64 Offset = parent.DirectoryEntry[parent.OffsetField].ValueOffset.ValueArray.Values[Index]; u64 ByteCount = parent.DirectoryEntry[parent.ByteCountField].ValueOffset.ValueArray.Values[Index]; std::mem::Bytes ImageData @ Offset [[name(std::format("{} {}", Desc, Index))]]; } [[inline]]; u64 currentIFD = 0; struct IFD { u64 Number = currentIFD; Big NumberDirectoryEntries; IFDEntry DirectoryEntry[NumberDirectoryEntries]; Big NextIFD; try { u64 OffsetField = get_field(DirectoryEntry, TIFFTag::StripOffsets); u64 ByteCountField = get_field(DirectoryEntry, TIFFTag::StripByteCounts); u64 Count = std::core::member_count(DirectoryEntry[OffsetField].ValueOffset.ValueArray.Values); ImageData<"Strip"> Strips[Count]; } catch {} try { u64 OffsetField = get_field(DirectoryEntry, TIFFTag::TileOffsets); u64 ByteCountField = get_field(DirectoryEntry, TIFFTag::TileByteCounts); u64 Count = std::core::member_count(DirectoryEntry[OffsetField].ValueOffset.ValueArray.Values); ImageData<"Tile"> Tiles[Count]; } catch {} } [[name(std::format("IFD {}", Number))]]; struct IFDS { IFD IFD; if (IFD.NextIFD > 0) { currentIFD += 1; IFDS IFD_tmp @ IFD.NextIFD; } } [[inline]]; struct TIFFFile { TIFFHeader Header; IFDS @ Header.Offset; }; TIFFFile File @ 0x00;