#pragma description Tag Image File Format (TIFF) #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(auto v) { return std::format("{} (0x{:X})", v, v); }; fn format_write_big(str v) { return std::string::parse_int(v, 0); }; fn transform_big(auto 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, StripRowCounts = 0x022F, // 559 XMP = 0x02BC, // 700 ImageRating = 0x4746, // 18246 ImageRatingPercent = 0x4749, // 18249 ImageID = 0x800D, // 32781 WangAnnotation = 0x80A4, // 32932 CFARepeatPatternDim = 0x828D, // 33421 CFAPattern = 0x828E, // 33422 BatteryLevel = 0x828F, // 33423 Copyright = 0x8298, ExposureTime = 0x829A, // 33434 FNumber = 0x829D, // 33437 MDFileTag = 0x82A5, // 33445 MDScalePixel = 0x82A6, // 33446 MDColorTable = 0x82A7, // 33447 MDLabName = 0x82A8, // 33448 MDSampleInfo = 0x82A9, // 33449 MDPrepDate = 0x82AA, // 33450 MDPrepTime = 0x82AB, // 33451 MDFileUnits = 0x82AC, // 33452 ModelPixelScaleTag = 0x830E, // 33550 IPTCNAA = 0x83BB, // 33723 INGRPacketDataTag = 0x847E, // 33918 INGRFlagRegisters = 0x847F, // 33919 IrasBTransformationMatrix = 0x8480, // 33920 ModelTiepointTag = 0x8482, // 33922 Site = 0x84E0, // 34016 ColorSequence = 0x84E1, // 34017 IT8Header = 0x84E2, // 34018 RasterPadding = 0x84E3, // 34019 BitsPerRunLength = 0x84E4, // 34020 BitsPerExtendedRunLength = 0x84E5, // 34021 ColorTable = 0x84E6, // 34022 ImageColorIndicator = 0x84E7, // 34023 BackgroundColorIndicator = 0x84E8, // 34024 ImageColorValue = 0x84E9, // 34025 BackgroundColorValue = 0x84EA, // 34026 PixelIntensityRange = 0x84EB, // 34027 TransparencyIndicator = 0x84EC, // 34028 ColorCharacterization = 0x84ED, // 34029 HCUsage = 0x84EE, // 34030 TrapIndicator = 0x84EF, // 34031 CMYKEquivalent = 0x84F0, // 34032 Reserved1 = 0x84F1, // 34033 Reserved2 = 0x84F2, // 34034 Reserved3 = 0x84F3, // 34035 ModelTransformationTag = 0x85D8, // 34264 Photoshop = 0x8649, // 34377 ExifIFD = 0x8769, // 34665 ICCProfile = 0x8773, ImageLayer = 0x87AC, // 34732 GeoKeyDirectoryTag = 0x87AF, // 34735 GeoDoubleParamsTag = 0x87B0, // 34736 GeoAsciiParamsTag = 0x87B1, // 34737 ExposureProgram = 0x8822, // 34850 SpectralSensitivity = 0x8824, // 34852 GPSInfo = 0x8825, // 34853 ISOSpeedRatings = 0x8827, // 34855 OECF = 0x8828, // 34856 Interlace = 0x8829, // 34857 TimeZoneOffset = 0x882A, // 34858 SelfTimeMode = 0x882B, // 34859 SensitivityType = 0x8830, // 34864 StandardOutputSensitivity = 0x8831, // 34865 RecommendedExposureIndex = 0x8832, // 34866 ISOSpeed = 0x8833, // 34867 ISOSpeedLatitudeyyy = 0x8834, // 34868 ISOSpeedLatitudezzz = 0x8835, // 34869 HylaFAXFaxRecvParams = 0x885C, // 34908 HylaFAXFaxSubAddress = 0x885D, // 34909 HylaFAXFaxRecvTime = 0x885E, // 34910 ExifVersion = 0x9000, // 36864 DateTimeOriginal = 0x9003, // 36867 DateTimeDigitized = 0x9004, // 36868 ComponentsConfiguration = 0x9101, // 37121 CompressedBitsPerPixel = 0x9102, // 37122 ShutterSpeedValue = 0x9201, // 37377 ApertureValue = 0x9202, // 37378 BrightnessValue = 0x9203, // 37379 ExposureBiasValue = 0x9204, // 37380 MaxApertureValue = 0x9205, // 37381 SubjectDistance = 0x9206, // 37382 MeteringMode = 0x9207, // 37383 LightSource = 0x9208, // 37384 Flash = 0x9209, // 37385 FocalLength = 0x920A, // 37386 FlashEnergy = 0x920B, // 37387 SpatialFrequencyResponse = 0x920C, // 37388 Noise = 0x920D, // 37389 FocalPlaneXResolution = 0x920E, // 37390 FocalPlaneYResolution = 0x920F, // 37391 FocalPlaneResolutionUnit = 0x9210, // 37392 ImageNumber = 0x9211, // 37393 SecurityClassification = 0x9212, // 37394 ImageHistory = 0x9213, // 37395 SubjectLocation = 0x9214, // 37396 ExposureIndex = 0x9215, // 37397 TIFFEPStandardID = 0x9216, // 37398 SensingMethod = 0x9217, // 37399 MakerNote = 0x927C, // 37500 UserComment = 0x9286, // 37510 SubsecTime = 0x9290, // 37520 SubsecTimeOriginal = 0x9291, // 37521 SubsecTimeDigitized = 0x9292, // 37522 ImageSourceData = 0x935C, // 37724 XPTitle = 0x9C9B, // 40091 XPComment = 0x9C9C, // 40092 XPAuthor = 0x9C9D, // 40093 XPKeywords = 0x9C9E, // 40094 XPSubject = 0x9C9F, // 40095 FlashpixVersion = 0xA000, // 40960 ColorSpace = 0xA001, // 40961 PixelXDimension = 0xA002, // 40962 PixelYDimension = 0xA003, // 40963 RelatedSoundFile = 0xA004, // 40964 InteroperabilityIFD = 0xA005, // 40965 FlashEnergyExif = 0xA20B, // 41483 SpatialFrequencyResponseExif = 0xA20C,// 41484 FocalPlaneXResolutionExif = 0xA20E, // 41486 FocalPlaneYResolutionExif = 0xA20F, // 41487 FocalPlaneResolutionUnitExif = 0xA210,// 41488 SubjectLocationExif = 0xA214, // 41492 ExposureIndexExif = 0xA215, // 41493 SensingMethodExif = 0xA217, // 41495 FileSource = 0xA300, // 41728 SceneType = 0xA301, // 41729 CFAPatternExif = 0xA302, // 41730 CustomRendered = 0xA401, // 41985 ExposureMode = 0xA402, // 41986 WhiteBalance = 0xA403, // 41987 // Adobe DNG and cDNG Extensions (0xC6, 0xc7) DNGVersion = 0xC612, // 50706 DNGBackwardVersion = 0xC613, // 50707 UniqueCameraModel = 0xC614, // 50708 LocalizedCameraModel = 0xC615, // 50709 CFALayout = 0xC617, // 50711 LinearizationTable = 0xC618, // 50712 BlackLevelRepeatDim = 0xC619, // 50713 BlackLevel = 0xC61A, // 50714 BlackLevelDeltaH = 0xC61B, // 50715 BlackLevelDeltaV = 0xC61C, // 50716 WhiteLevel = 0xC61D, // 50717 DefaultScale = 0xC61E, // 50718 DefaultCropOrigin = 0xC61F, // 50719 DefaultCropSize = 0xC620, // 50720 ColorMatrix1 = 0xC621, // 50721 ColorMatrix2 = 0xC622, // 50722 CameraCalibration1 = 0xC623, // 50723 CameraCalibration2 = 0xC624, // 50724 ReductionMatrix1 = 0xC625, // 50725 ReductionMatrix2 = 0xC626, // 50726 AnalogBalance = 0xC627, // 50727 AsShotNeutral = 0xC628, // 50728 AsShotWhiteXY = 0xC629, // 50729 BaselineExposure = 0xC62A, // 50730 BaselineNoise = 0xC62B, // 50731 BaselineSharpness = 0xC62C, // 50732 BayerGreenSplit = 0xC62D, // 50733 LinearResponseLimit = 0xC62E, // 50734 CameraSerialNumber = 0xC62F, // 50735 LensInfo = 0xC630, // 50736 ChromaBlurRadius = 0xC631, // 50737 AntiAliasStrength = 0xC632, // 50738 ShadowScale = 0xC633, // 50739 DNGPrivateData = 0xC634, // 50740 MakerNoteSafety = 0xC635, // 50741 CalibrationIlluminant1 = 0xC65A, // 50778 CalibrationIlluminant2 = 0xC65B, // 50779 BestQualityScale = 0xC65C, // 50780 RawDataUniqueID = 0xC65D, // 50781 OriginalRawFileName = 0xC68B, // 50827 OriginalRawFileData = 0xC68C, // 50828 ActiveArea = 0xC68D, // 50829 MaskedAreas = 0xC68E, // 50830 AsShotICCProfile = 0xC68F, // 50831 AsShotPreProfileMatrix = 0xC690, // 50832 CurrentICCProfile = 0xC691, // 50833 CurrentPreProfileMatrix = 0xC692, // 50834 ColorimetricReference = 0xC6BF, // 50879 CameraCalibrationSignature = 0xC6F3, // 50931 ProfileCalibrationSignature = 0xC6F4, // 50932 ExtraCameraProfiles = 0xC6F6, // 50934 AsShotProfileName = 0xC6F8, // 50936 NoiseReductionApplied = 0xC6F9, // 50937 ProfileName = 0xC6FA, // 50938 ProfileHueSatMapDims = 0xC6FB, // 50939 ProfileHueSatMapData1 = 0xC6FC, // 50940 ProfileHueSatMapData2 = 0xC6FD, // 50941 ProfileToneCurve = 0xC6FE, // 50942 ProfileEmbedPolicy = 0xC6FF, // 50943 ProfileCopyright = 0xC700, // 50944 ForwardMatrix1 = 0xC714, // 50964 ForwardMatrix2 = 0xC715, // 50965 PreviewApplicationName = 0xC716, // 50966 PreviewApplicationVersion = 0xC717, // 50967 PreviewSettingsName = 0xC718, // 50968 PreviewSettingsDigest = 0xC719, // 50969 PreviewColorSpace = 0xC71A, // 50970 PreviewDateTime = 0xC71B, // 50971 RawImageDigest = 0xC71C, // 50972 OriginalRawFileDigest = 0xC71D, // 50973 SubTileBlockSize = 0xC71E, // 50974 RowInterleaveFactor = 0xC71F, // 50975 ProfileLookTableDims = 0xC725, // 50981 ProfileLookTableData = 0xC726, // 50982 OpcodeList1 = 0xC740, // 51008 OpcodeList2 = 0xC741, // 51009 OpcodeList3 = 0xC74E, // 51022 NoiseProfile = 0xC761, // 51041 TimeCodes = 0xC763, // 51043 FrameRate = 0xc764, // 51044 TStop = 0xc772, // 51058 OriginalDefaultFinalSize = 0xC791, // 51089 OriginalBestQualityFinalSize = 0xC792,// 51090 OriginalDefaultCropSize = 0xC793, // 51091 ProfileHueSatMapEncoding = 0xC7A3, // 51107 ProfileLookTableEncoding = 0xC7A4, // 51108 BaselineExposureOffset = 0xC7A5, // 51109 DefaultBlackRender = 0xC7A6, // 51110 NewRawImageDigest = 0xC7A7, // 51111 RawToPreviewGain = 0xC7A8, // 51112 DefaultUserCrop = 0xC7B5, // 51125 DepthFormat = 0xC7E9, // 51177 DepthNear = 0xC7EA, // 51178 DepthFar = 0xC7EB, // 51179 DepthUnits = 0xC7EC, // 51180 DepthMeasureType = 0xC7ED, // 51181 EnhanceParams = 0xC7EE // 51182 }; 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;