Search code examples
reverse-engineeringpsdvideo-codecs

reverse engineering lossless video codec (photoshop format video layer)


I'm implementing decoding photoshop (PSD) files. They have the format mostly documented, but not all. In newer versions of photoshop you can have video layers. And the video layer data is stored within a 'PxSD' tag (the contents are not documented). I have managed to reverse engineer some of the metadata with the following code:

            case "PxSD": {
                // Raw data for 3D or video layers.
                var length = stream.readUint64();

                var layer_id = stream.readUint32(); // id of video layer

                var unknown1 = stream.readUint32(); // = 2
                var unknown2 = stream.readUint32(); // = 2

                var size = stream.readUint64(); // remaining size

                var numFrames = stream.readUint32();

                for (var nframe = 0; nframe < numFrames; ++nframe)
                {
                    var size2 = stream.readUint64();
                    var absFrame = stream.readUint32();
                    var layerTop = stream.readUint32();
                    var layerLeft = stream.readUint32();

                    // TODO: mode, 1 for grayscale image, 3 = RGB image
                    // They are always same?
                    var mode = stream.readUint32();
                    var mode2 = stream.readUint32();

                    // TODO: numChannels
                    // 4 if painting on a frame from blank video layer
                    // 6 if painting on top of an imported RGB video
                    var channels = 4;

                    for (var ch = 0; ch < channels; ++ch)
                    {
                        var depth = stream.readUint32();    // 8
                        var top = stream.readInt32();     // layer dimensions
                        var left = stream.readInt32();
                        var bottom = stream.readInt32();
                        var right = stream.readInt32();

                        var unknown3 = stream.readUint32();
                        let sz = stream.readUint32();

                        var dt = new Uint8Array(sz);
                        stream.readUint8Array(dt);
                        console.log(dt);
                    }
                }
                break;
            }

A few examples of the contents of the actual compressed data

width: 1, height: 1 - Entirely black (0) channel

[0, 3, 72, 137, 250, 15, 16, 96, 0, 1, 0, 1, 0, 0, 0, 0] (length=16)

width: 1, height: 1 - Entirely white (255) channel

[0, 3, 72, 137, 98, 0, 8, 48, 0, 0, 1, 0, 1, 0, 0, 0] (length=16)

width: 96, height: 8 - Entirely black (0) channel

[0, 3, 72, 137, 250, 207, 64, 91, 240, 127, 212, 252, 81, 243, 7, 177, 249, 0, 1, 6, 0, 118, 67, 7, 249, 0, 0, 0]

width: 96, height: 8 - Entirely white (255) channel

[0, 3, 72, 137, 98, 96, 24, 5, 163, 96, 228, 2, 128, 0, 3, 0, 3, 0, 0, 1]

width: 96, height: 7 - Entirely black (0) channel

[0, 3, 72, 137, 250, 207, 64, 91, 240, 127, 212, 252, 81, 243, 41, 0, 0, 1, 6, 0, 120, 182, 6, 250]

Does anyone have any thoughts of what kind of compression is used ? (it is most certainly lossless) (are they using macroblocks, some kind of lossless JPEG?, is it just zlib?) anyone point me in right direction? what does it look like? Ideas?

Thank you


Solution

  • Ok, it seems like the first 2 bytes in the data is the compression

    0=uncompressed
    1=RLE
    2=ZLIB
    3=ZLIB with prediction
    

    Haven't thoroughly tested yet