feat: render `.mdp` thumbnails.
Summary
Add support for rendering .mdp thumbnails.
Fixes https://github.com/TagStudioDev/TagStudio/issues/1145
File layout
Mostly translated from the repo linked below.
The first 20 bytes are a binary header called TMDIPack.
struct TMDIPack {
char header[8]; // "mdipack "
unsigned long version; // always 0
unsigned long mdiSize; // size of the XML header
unsigned long mdibinSize; // size of the actual project data
}
After the binary header comes an XML header called MDI.
<?xml version="1.0" encoding="UTF-8" ?>
<Mdiapp width="1600" height="1200" dpi="350" checkerBG="true" bgColorR="255" bgColorG="255" bgColorB="255">
<CreateTime time="1758967935" timeString="2025-09-27T12:12:15" />
<UpdateTime time="1758967935" timeString="2025-09-27T12:12:15" rev="1" />
<Thumb width="256" height="192" bin="thumb" />
<Snaps />
<Guides />
<ICCProfiles enabled="false" cmykView="false" blackPoint="true" renderingIntent="perceptual" />
<Layers active="1">
<Layer ofsx="0" ofsy="0" width="1600" height="1200" mode="normal" alpha="255" visible="true" protectAlpha="false" locked="false" clipping="false" masking="false" maskingType="0" group="-1" id="0" draft="false" parentId="-1" name="Ebene1" binType="2" bin="layer0img" type="32bpp" />
<Layer ofsx="0" ofsy="0" width="1600" height="1200" mode="normal" alpha="255" visible="true" protectAlpha="false" locked="false" clipping="false" masking="false" maskingType="0" group="-1" id="1" draft="false" parentId="-1" name="Ebene2" binType="2" bin="layer1img" type="32bpp" />
</Layers>
<appInfo name="MediBang Paint Pro" rev="92" bitType="64" />
</Mdiapp>
The relevant bits are the Thumb element and its attributes as well as the amount of Layer elements.
After the XML comes the MDIBIN which holds the embedded thumbnail as well as the actual project data.
The MDIBIN contains with a 132-byte TPackerHeader followed by a blob of data whose length is specified in the header.
This repeats for each layer as well as the thumbnail.
The TPackerHeader looks like this
struct TPackerHeader {
char identifier[4]; // 'PAC '
unsigned long chunkSIze; // Size of the TPackerHeader and layer / thumbnail data
unsigned long streamType; // 0 for uncompressed, 1 for zlib
unsigned long streamSize; // Size of the layer / thumbnail data
unsigned long outSize; // Size after decompression
unsigned char reserved[48]; // Buffer, all 0
unsigned char archiveName[64]; // Name of the layer, padded. The one of the thumbnail is called "thumb", otherwise it's the layer's "bin" attribute from the xml header.
}
The thumbnail is stored as a continious blob of BGRA image data whose dimensions are specified in the attributes of the Thumb element in the XML header.
I think the thumbnail is always the first block in the MDIBIN, but I couldn't find anything explicitly saying so.
While the thumbnail is in BGRA, it doesn't seem to store any transparency data. It always shows up with a solid white background, even in Windows explorer.
I also wanted to document the format in the code, but I always ended up with a docstring several times the size of the actual implementation, so I left it out.
Before
After
Sources for the file format
I Got most of the info from this repo. It's in Japanese, so I translated it via Deepl. It also gets one thig wrong, namely the color mode of the thumbnail. It claims its in ARGB, when it's actually in BGRA. I found that out via this GIMP plugin for .mdp files
File used for testing
layers-color-transparency.zip Zipped because of GitHub upload limitations,
Tasks Completed
- Platforms Tested:
- [ ] Windows x86
- [ ] Windows ARM
- [ ] macOS x86
- [ ] macOS ARM
- [x] Linux x86
- [ ] Linux ARM
- Tested For:
- [x] Basic functionality
- [ ] PyInstaller executable
Holy shit, thank you so much, I had no idea there was an existing project that already worked out how the file format was laid out