TagStudio icon indicating copy to clipboard operation
TagStudio copied to clipboard

feat: render `.mdp` thumbnails.

Open Sola-ris opened this issue 3 months ago • 1 comments

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. 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

before

After

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

Sola-ris avatar Sep 28 '25 10:09 Sola-ris

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

TrigamDev avatar Sep 28 '25 13:09 TrigamDev