diff --git a/lib/ivis_opengl/png_util.cpp b/lib/ivis_opengl/png_util.cpp index 330c60d752f..cb718207ab8 100644 --- a/lib/ivis_opengl/png_util.cpp +++ b/lib/ivis_opengl/png_util.cpp @@ -574,7 +574,7 @@ IMGSaveError iV_loadImage_PNG(const std::vector& memoryBuffer, iV // Note: This function must be thread-safe. // It does not call the debug() macro directly, but instead returns an IMGSaveError structure with the text of any error. -static IMGSaveError internal_saveImage_PNG(const char *fileName, const iV_Image *image, int color_type) +static IMGSaveError internal_saveImage_PNG(const char *fileName, const iV_Image *image, int color_type, bool bottom_up_write = true) { unsigned char **volatile scanlines = nullptr; // Must be volatile to reliably preserve value if modified between setjmp/longjmp. png_infop info_ptr = nullptr; @@ -665,9 +665,16 @@ static IMGSaveError internal_saveImage_PNG(const char *fileName, const iV_Image unsigned char* bitmap_ptr = const_cast(image->bmp()); for (currentRow = 0; currentRow < image->height(); ++currentRow) { - // We're filling the scanline from the bottom up here, - // otherwise we'd have a vertically mirrored image. - scanlines[currentRow] = &bitmap_ptr[row_stride * (image->height() - currentRow - 1)]; + if (bottom_up_write) + { + // We're filling the scanline from the bottom up here, + // otherwise we'd have a vertically mirrored image. + scanlines[currentRow] = &bitmap_ptr[row_stride * (image->height() - currentRow - 1)]; + } + else + { + scanlines[currentRow] = &bitmap_ptr[row_stride * currentRow]; + } } png_set_rows(png_ptr, info_ptr, (png_bytepp)scanlines); @@ -756,3 +763,38 @@ void iV_saveImage_JPEG(const char *fileName, const iV_Image *image) PHYSFS_close(fileHandle); } +// Note: This function is *NOT* thread-safe. +bool iV_LoadAndSavePNG_AsLumaSingleChannel(const std::string &inputFilename, const std::string &outputFilename, bool check) +{ + iV_Image image; + + // 1.) Load the PNG into an iV_Image + if (!iV_loadImage_PNG2(inputFilename.c_str(), image, false, false)) + { + // Failed to load the input image + return false; + } + + bool result = image.convert_to_luma(); + ASSERT_OR_RETURN(false, result, "(%s): Failed to convert specular map", inputFilename.c_str()); + + auto saveResult = internal_saveImage_PNG(outputFilename.c_str(), &image, PNG_COLOR_TYPE_GRAY, false); + ASSERT_OR_RETURN(false, saveResult.noError(), "(%s): Failed to save specular map output, with error: %s", outputFilename.c_str(), saveResult.text.c_str()); + + if (check) + { + // Test round-trip - does loading the written image yield the same iV_Image bitmap data? + iV_Image outputImage; + if (!iV_loadImage_PNG2(outputFilename.c_str(), outputImage, false, false)) + { + // Failed to load the output image + return false; + } + + ASSERT_OR_RETURN(false, outputImage.channels() == 1, "Output image unexpectedly has %u channels", outputImage.channels()); + ASSERT_OR_RETURN(false, image.compare_equal(outputImage), "Output image doesn't seem to match expected"); + debug(LOG_INFO, "Validated that output image loads the same bitmap data that was saved."); + } + + return true; +} diff --git a/lib/ivis_opengl/png_util.h b/lib/ivis_opengl/png_util.h index 3df0197fca5..e4db3cf54fe 100644 --- a/lib/ivis_opengl/png_util.h +++ b/lib/ivis_opengl/png_util.h @@ -86,4 +86,7 @@ IMGSaveError iV_saveImage_PNG_Gray(const char *fileName, const iV_Image *image); void iV_saveImage_JPEG(const char *fileName, const iV_Image *image); +// For loading and outputting multi-channel (ex. RGB) specular maps as WZ-converted single-channel luma grayscale PNGs +bool iV_LoadAndSavePNG_AsLumaSingleChannel(const std::string &inputFilename, const std::string &outputFilename, bool check = false); + #endif // _LIBIVIS_COMMON_PNG_H_