Skip to content

fix(Texture2DConverter): use a valid Pillow raw mode for R16 - #394

Open
com55 wants to merge 1 commit into
K0lb3:masterfrom
com55:fix/r16-pillow-rawmode
Open

com55 wants to merge 1 commit into
K0lb3:masterfrom
com55:fix/r16-pillow-rawmode

Conversation

@com55

@com55 com55 commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Fixes #393

TF.R16 was decoded with the Pillow raw mode R;16 for an RGB image. Pillow has no such
raw mode, so Texture2D.image raised ValueError: unknown raw mode for given image mode
for every R16 texture.

This changes it to R;16L (little-endian 16-bit, high byte kept), so the value lands in the
red channel at 8 bit, the same as R8 and the same 8-bit R output the encoder side
(TF.R16 -> TF.R8) already assumes.

Tests
tests/test_texture2d_r16.py decodes a 2x2 R16 buffer through parse_image_data and checks the
red channel equals each value's high byte and G/B are 0. It fails on master with the error above
and passes with this change. I only ran this test file, not the full suite (the repo's samples
are stored with git LFS and were not available where I tested).

Also verified on a real texture (512x512, 10 mips):
decoded red channel equals the high byte of the raw data for every pixel.

Open questions for review

  1. 8-bit vs 16-bit: R;16L drops the low byte. Decoding to mode I;16 (("I;16", "raw", "I;16"))
    would keep full precision but changes the output image mode from RGB, which callers of
    Texture2D.image may not expect. Is 8-bit RGB the intended contract here?
  2. Little vs big endian: assumed little-endian (R;16L), like the other 16-bit formats; the
    real sample matched. Should big-endian platforms (e.g. Xbox 360 / XBOX_SWAP_FORMATS) be handled separately?
  3. Encoding: set_image for R16 falls into the group that re-encodes as TF.R8 with
    pil_mode = "R" (Texture2DConverter.py ~lines 178-185). From reading the code I believe writing
    an image back to an R16 texture yields 8-bit data; I have not tested this round trip.
  4. Other broken entries: the same Image.frombytes check fails for R8_SIGNED, R16_SIGNED,
    RGB24_SIGNED, RGBA32_SIGNED, RGB48_SIGNED, RGBA64_SIGNED, RGBA64, RFloat,
    RGBAFloat, ARGBFloat (list in the issue). Left out of this PR to keep it focused; happy to
    handle them here or separately - which do you prefer?

Pillow has no "R;16" raw mode for RGB images, so decoding any R16 texture
raised "unknown raw mode for given image mode". Use "R;16L" (little-endian,
high byte kept), consistent with R8 decoding into the red channel.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Texture2D.image fails for R16 textures: "unknown raw mode for given image mode"

1 participant