tinygltf icon indicating copy to clipboard operation
tinygltf copied to clipboard

[TODO] Support 16bit PNG write

Open syoyo opened this issue 5 years ago • 11 comments

It is useful to support writing 16bit PNG for:

  • gltfutil(texture dumper)
  • Serialize glTF

Extending stb_image_write looks rather complex, so our bet is using LodePNG.

syoyo avatar Mar 03 '19 04:03 syoyo

Created a branch. https://github.com/syoyo/tinygltf/tree/16bit-lodepng

16 bit PNG image(e.g. Normal map in AntiqueCamera) generated by gltfutil is wrong

syoyo avatar Mar 03 '19 08:03 syoyo

I agree with you, stb_image_write, as its author says is just a "quick and dirty" tool https://github.com/nothings/stb/issues/605#issuecomment-434913282

For LodePNG, I am unsure, don't you think it would be valuable to find a library that doesn't support "only" PNG images for serializing glTF files? Or do you want to use LodePNG only in the case that we are writing out PNG files, and rely in stb_write for the other formats like we do today?

Ybalrid avatar Mar 03 '19 12:03 Ybalrid

Normal map generated by gltfutil is wrong

The reason was lodepng::encode expects bit endian image data. I got a proper result by reordering pixel data into big endian manner: https://github.com/syoyo/tinygltf/commit/758a1240c938e2c644936924abdd9909ab4704c9

syoyo avatar Mar 03 '19 12:03 syoyo

@Ybalrid I have added 16bit PNG write feature using LodePNG(and also write to OpenEXR format using tinyexr as a bonus). Please take a look https://github.com/syoyo/tinygltf/tree/16bit-lodepng

Now read and write looks working well. Will merge it into master in the near future.

syoyo avatar Mar 03 '19 12:03 syoyo

I see what you did.

It's not super important, but there's maybe some slightly more efficient manner to byte-swap these from little-endian to big-endian using bitwise operators and bitshifts

e.g: if buffer here is an array of unsigned shorts (or just an usigned short* with the address of the start of the byte array)

for(size_t i = 0; i < buffer.size(); ++i)
    buffer[i] = ((0xFF00 & buffer[i]) >> 8) | ((0x00FF & buffer[i]) << 8);

If each numbers starts like high byte; low byte this will put the low byte into the high byte and the high byte into the low byte, and perform an OR between low byte; 0 and 0; high byte -> low byte; high byte.

No need to have a temporary variable of call to a function (lambda) doing so

Ybalrid avatar Mar 03 '19 14:03 Ybalrid

(also the tinyexr header commited into that branch doesn't have a "SaveEXR" function that takes a boolean to tell if we are 16bit)

Ybalrid avatar Mar 03 '19 14:03 Ybalrid

diff --git a/examples/gltfutil/texture_dumper.cc b/examples/gltfutil/texture_dumper.cc
index c46bc70..ff91e1b 100644
--- a/examples/gltfutil/texture_dumper.cc
+++ b/examples/gltfutil/texture_dumper.cc
@@ -44,21 +44,11 @@ static void ToBigEndian(std::vector<uint8_t>* image) {
     return;
   }
 
-  auto swap2 =
-      [](uint16_t* val) {
-        uint16_t tmp = *val;
-        uint8_t* dst = reinterpret_cast<uint8_t*>(val);
-        uint8_t* src = reinterpret_cast<uint8_t*>(&tmp);
-
-        dst[0] = src[1];
-        dst[1] = src[0];
-      };
-
   uint16_t *ptr = reinterpret_cast<uint16_t *>(image->data());
   size_t n = image->size() / 2;
 
   for (size_t i = 0; i < n; i++) {
-    swap2(&ptr[i]);
+    ptr[i] = ((0xFF00 & ptr[i]) >> 8) | ((0x00FF & ptr[i]) << 8);
   }
 }
 

You can put the in a textfile.patch and run "git apply-path" on it if you want, or I can PR this change against the 16bit-lodepng branch if you prefer.

Ybalrid avatar Mar 03 '19 15:03 Ybalrid

I've created a PR against the 16bit-lodepng branch

Ybalrid avatar Mar 03 '19 15:03 Ybalrid

Merged! > I've created a PR against the 16bit-lodepng branch

(also the tinyexr header commited into that branch doesn't have a "SaveEXR" function that takes a boolean to tell if we are 16bit)

Sorry, I have forgotten to commit tinyexr.h. Now it should be ok: https://github.com/syoyo/tinygltf/commit/8fd91aea04510808885f1a05e28a84fb67c4c999

syoyo avatar Mar 03 '19 17:03 syoyo

So, as of now we have code that can save a 16bit png, or a 16bit EXR image inside the gltfutil program. Do we have this functionality working for serializing glTF to disk in tiny_gltf.h ?

If not, that's the next thing to add IMHO.

Ybalrid avatar Mar 03 '19 17:03 Ybalrid

Do we have this functionality working for serializing glTF to disk in tiny_gltf.h ?

Not yet. PR is appreciated!

syoyo avatar Mar 04 '19 05:03 syoyo