imageio: split HDRDecoder into its own file
This commit is contained in:
@@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.19)
|
||||
project(filament-utils-android)
|
||||
|
||||
set(FILAMENT_DIR ${FILAMENT_DIST_DIR})
|
||||
set(IMAGEIO_DIR ../../libs/imageio)
|
||||
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../gltfio-android ${CMAKE_CURRENT_BINARY_DIR}/gltfio-android)
|
||||
|
||||
@@ -35,14 +36,21 @@ add_library(filament-utils-jni SHARED
|
||||
src/main/cpp/Manipulator.cpp
|
||||
src/main/cpp/RemoteServer.cpp
|
||||
|
||||
${IMAGEIO_DIR}/include/imageio/ImageDecoder.h
|
||||
${IMAGEIO_DIR}/include/imageio/HDRDecoder.h
|
||||
${IMAGEIO_DIR}/src/HDRDecoder.cpp
|
||||
|
||||
../common/CallbackUtils.cpp
|
||||
../common/NioUtils.cpp
|
||||
)
|
||||
|
||||
target_compile_definitions(filament-utils-jni PUBLIC IMAGEIO_LITE=1)
|
||||
|
||||
target_include_directories(filament-utils-jni PRIVATE
|
||||
${FILAMENT_DIR}/include
|
||||
..
|
||||
../../third_party/stb
|
||||
${IMAGEIO_DIR}/include
|
||||
../../libs/utils/include)
|
||||
|
||||
set_target_properties(filament-utils-jni PROPERTIES LINK_DEPENDS
|
||||
|
||||
@@ -9,6 +9,7 @@ set(PUBLIC_HDR_DIR include)
|
||||
# ==================================================================================================
|
||||
set(PUBLIC_HDRS
|
||||
include/imageio/BlockCompression.h
|
||||
include/imageio/HDRDecoder.h
|
||||
include/imageio/ImageDecoder.h
|
||||
include/imageio/ImageDiffer.h
|
||||
include/imageio/ImageEncoder.h
|
||||
@@ -16,6 +17,7 @@ set(PUBLIC_HDRS
|
||||
|
||||
set(SRCS
|
||||
src/BlockCompression.cpp
|
||||
src/HDRDecoder.cpp
|
||||
src/ImageDecoder.cpp
|
||||
src/ImageDiffer.cpp
|
||||
src/ImageEncoder.cpp
|
||||
|
||||
47
libs/imageio/include/imageio/HDRDecoder.h
Normal file
47
libs/imageio/include/imageio/HDRDecoder.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef IMAGE_HDRDECODER_H_
|
||||
#define IMAGE_HDRDECODER_H_
|
||||
|
||||
#include <imageio/ImageDecoder.h>
|
||||
|
||||
namespace image {
|
||||
|
||||
class HDRDecoder : public ImageDecoder::Decoder {
|
||||
public:
|
||||
static HDRDecoder* create(std::istream& stream);
|
||||
static bool checkSignature(char const* buf);
|
||||
|
||||
HDRDecoder(const HDRDecoder&) = delete;
|
||||
HDRDecoder& operator=(const HDRDecoder&) = delete;
|
||||
|
||||
private:
|
||||
explicit HDRDecoder(std::istream& stream);
|
||||
~HDRDecoder() override;
|
||||
|
||||
// ImageDecoder::Decoder interface
|
||||
LinearImage decode() override;
|
||||
|
||||
static const char sigRadiance[];
|
||||
static const char sigRGBE[];
|
||||
std::istream& mStream;
|
||||
std::streampos mStreamStartPos;
|
||||
};
|
||||
|
||||
} // namespace image
|
||||
|
||||
#endif /* IMAGE_IMAGEDECODER_H_ */
|
||||
200
libs/imageio/src/HDRDecoder.cpp
Normal file
200
libs/imageio/src/HDRDecoder.cpp
Normal file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <imageio/HDRDecoder.h>
|
||||
|
||||
#include <image/ImageOps.h>
|
||||
|
||||
#include <math/vec3.h>
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
// for ntohs
|
||||
#if defined(WIN32)
|
||||
# include <Winsock2.h>
|
||||
# include <utils/unwindows.h>
|
||||
#else
|
||||
# include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
using namespace utils;
|
||||
|
||||
namespace image {
|
||||
|
||||
const char HDRDecoder::sigRadiance[] = { '#', '?', 'R', 'A', 'D', 'I', 'A', 'N', 'C', 'E', 0xa };
|
||||
const char HDRDecoder::sigRGBE[] = { '#', '?', 'R', 'G', 'B', 'E', 0xa };
|
||||
|
||||
HDRDecoder* HDRDecoder::create(std::istream& stream) {
|
||||
HDRDecoder* decoder = new HDRDecoder(stream);
|
||||
return decoder;
|
||||
}
|
||||
|
||||
bool HDRDecoder::checkSignature(char const* buf) {
|
||||
return !memcmp(buf, sigRadiance, sizeof(sigRadiance)) ||
|
||||
!memcmp(buf, sigRGBE, sizeof(sigRGBE));
|
||||
}
|
||||
|
||||
HDRDecoder::HDRDecoder(std::istream& stream)
|
||||
: mStream(stream), mStreamStartPos(stream.tellg()) {
|
||||
}
|
||||
|
||||
HDRDecoder::~HDRDecoder() = default;
|
||||
|
||||
LinearImage HDRDecoder::decode() {
|
||||
float gamma;
|
||||
float exposure;
|
||||
char sy, sx;
|
||||
unsigned int height = 0, width = 0;
|
||||
{
|
||||
char buf[1024];
|
||||
do {
|
||||
char format[128];
|
||||
mStream.getline(buf, sizeof(buf), 0xa);
|
||||
if (buf[0] == '#') continue;
|
||||
sscanf(buf, "FORMAT=%127s", format); // NOLINT
|
||||
sscanf(buf, "GAMMA=%f", &gamma); // NOLINT
|
||||
sscanf(buf, "EXPOSURE=%f", &exposure); // NOLINT
|
||||
if ((sscanf(buf, "%cY %u %cX %u", &sy, &height, &sx, &width) == 4)|| // NOLINT
|
||||
(sscanf(buf, "%cX %u %cY %u", &sx, &width, &sy, &height) == 4)) { // NOLINT
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
LinearImage image(width, height, 3);
|
||||
|
||||
if (sx == '-') image = (image);
|
||||
if (sy == '+') image = verticalFlip(image);
|
||||
|
||||
// Allocate memory to hold one row of decoded pixel data.
|
||||
std::unique_ptr<uint8_t[]> rgbe(new uint8_t[width * 4]);
|
||||
|
||||
// First, test for non-RLE images.
|
||||
const auto pos = mStream.tellg();
|
||||
mStream.read((char*) rgbe.get(), 3);
|
||||
mStream.seekg(pos);
|
||||
|
||||
if (rgbe[0] != 0x2 || rgbe[1] != 0x2 || (rgbe[2] & 0x80) || width < 8 || width > 32767) {
|
||||
for (uint32_t y = 0; y < height; y++) {
|
||||
filament::math::float3* dst = reinterpret_cast<filament::math::float3*>(image.getPixelRef(0, y));
|
||||
mStream.read((char*) rgbe.get(), width * 4);
|
||||
// (rgb/256) * 2^(e-128)
|
||||
size_t pixel = 0;
|
||||
for (size_t x = 0; x < width; x++, pixel += 4) {
|
||||
if (rgbe[pixel + 3] == 0.0f) {
|
||||
dst[x] = filament::math::float3{0.0f};
|
||||
} else {
|
||||
filament::math::float3 v(rgbe[pixel], rgbe[pixel + 1], rgbe[pixel + 2]);
|
||||
dst[x] = (v + 0.5f) * std::ldexp(1.0f, rgbe[pixel + 3] - (128 + 8));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (uint32_t y = 0; y < height; y++) {
|
||||
uint16_t magic;
|
||||
mStream.read((char*) &magic, 2);
|
||||
if (magic != 0x0202) {
|
||||
slog.e << "invalid scanline (magic)" << io::endl;
|
||||
return {};
|
||||
}
|
||||
|
||||
uint16_t w;
|
||||
mStream.read((char*) &w, 2);
|
||||
if (ntohs(w) != width) {
|
||||
slog.e << "invalid scanline (width)" << io::endl;
|
||||
return {};
|
||||
}
|
||||
|
||||
char* d = (char*) rgbe.get();
|
||||
for (size_t p = 0; p < 4; p++) {
|
||||
size_t num_bytes = 0;
|
||||
while (num_bytes < width) {
|
||||
uint8_t rle_count;
|
||||
mStream.read((char*) &rle_count, 1);
|
||||
if (rle_count > 128) {
|
||||
char v;
|
||||
mStream.read(&v, 1);
|
||||
memset(d, v, size_t(rle_count - 128));
|
||||
d += rle_count - 128;
|
||||
num_bytes += rle_count - 128;
|
||||
} else {
|
||||
if (rle_count == 0) {
|
||||
slog.e << "run length is zero" << io::endl;
|
||||
return {};
|
||||
}
|
||||
mStream.read(d, rle_count);
|
||||
d += rle_count;
|
||||
num_bytes += rle_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t const* r = &rgbe[0];
|
||||
uint8_t const* g = &rgbe[width];
|
||||
uint8_t const* b = &rgbe[2 * width];
|
||||
uint8_t const* e = &rgbe[3 * width];
|
||||
filament::math::float3* dst = reinterpret_cast<filament::math::float3*>(image.getPixelRef(0, y));
|
||||
// (rgb/256) * 2^(e-128)
|
||||
for (size_t x = 0; x < width; x++, r++, g++, b++, e++) {
|
||||
if (e[0] == 0.0f) {
|
||||
dst[x] = filament::math::float3{0.0f};
|
||||
} else {
|
||||
filament::math::float3 v(r[0], g[0], b[0]);
|
||||
dst[x] = (v + 0.5f) * std::ldexp(1.0f, e[0] - (128 + 8));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
#ifdef IMAGEIO_LITE
|
||||
|
||||
LinearImage ImageDecoder::decode(std::istream& stream, const std::string& sourceName,
|
||||
ColorSpace sourceSpace) {
|
||||
|
||||
Format format = Format::NONE;
|
||||
|
||||
std::streampos pos = stream.tellg();
|
||||
char buf[16];
|
||||
stream.read(buf, sizeof(buf));
|
||||
|
||||
if (HDRDecoder::checkSignature(buf)) {
|
||||
format = Format::HDR;
|
||||
}
|
||||
|
||||
stream.seekg(pos);
|
||||
|
||||
std::unique_ptr<Decoder> decoder;
|
||||
switch (format) {
|
||||
case Format::HDR:
|
||||
decoder.reset(HDRDecoder::create(stream));
|
||||
decoder->setColorSpace(ColorSpace::LINEAR);
|
||||
break;
|
||||
default:
|
||||
return LinearImage();
|
||||
}
|
||||
|
||||
return decoder->decode();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace image
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include <png.h>
|
||||
|
||||
// for ntohs
|
||||
#if defined(WIN32)
|
||||
# include <Winsock2.h>
|
||||
# include <utils/unwindows.h>
|
||||
@@ -44,6 +45,8 @@
|
||||
#include <image/ColorTransform.h>
|
||||
#include <image/ImageOps.h>
|
||||
|
||||
#include <imageio/HDRDecoder.h>
|
||||
|
||||
namespace image {
|
||||
|
||||
class PNGDecoder : public ImageDecoder::Decoder {
|
||||
@@ -77,29 +80,6 @@ private:
|
||||
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
|
||||
class HDRDecoder : public ImageDecoder::Decoder {
|
||||
public:
|
||||
static HDRDecoder* create(std::istream& stream);
|
||||
static bool checkSignature(char const* buf);
|
||||
|
||||
HDRDecoder(const HDRDecoder&) = delete;
|
||||
HDRDecoder& operator=(const HDRDecoder&) = delete;
|
||||
|
||||
private:
|
||||
explicit HDRDecoder(std::istream& stream);
|
||||
~HDRDecoder() override;
|
||||
|
||||
// ImageDecoder::Decoder interface
|
||||
LinearImage decode() override;
|
||||
|
||||
static const char sigRadiance[];
|
||||
static const char sigRGBE[];
|
||||
std::istream& mStream;
|
||||
std::streampos mStreamStartPos;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
|
||||
class PSDDecoder : public ImageDecoder::Decoder {
|
||||
public:
|
||||
static PSDDecoder* create(std::istream& stream);
|
||||
@@ -333,142 +313,6 @@ void PNGDecoder::error() {
|
||||
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
|
||||
const char HDRDecoder::sigRadiance[] = { '#', '?', 'R', 'A', 'D', 'I', 'A', 'N', 'C', 'E', 0xa };
|
||||
const char HDRDecoder::sigRGBE[] = { '#', '?', 'R', 'G', 'B', 'E', 0xa };
|
||||
|
||||
HDRDecoder* HDRDecoder::create(std::istream& stream) {
|
||||
HDRDecoder* decoder = new HDRDecoder(stream);
|
||||
return decoder;
|
||||
}
|
||||
|
||||
bool HDRDecoder::checkSignature(char const* buf) {
|
||||
return !memcmp(buf, sigRadiance, sizeof(sigRadiance)) ||
|
||||
!memcmp(buf, sigRGBE, sizeof(sigRGBE));
|
||||
}
|
||||
|
||||
HDRDecoder::HDRDecoder(std::istream& stream)
|
||||
: mStream(stream), mStreamStartPos(stream.tellg()) {
|
||||
}
|
||||
|
||||
HDRDecoder::~HDRDecoder() = default;
|
||||
|
||||
LinearImage HDRDecoder::decode() {
|
||||
try {
|
||||
float gamma;
|
||||
float exposure;
|
||||
char sy, sx;
|
||||
unsigned int height, width;
|
||||
|
||||
{
|
||||
char buf[1024];
|
||||
do {
|
||||
char format[128];
|
||||
mStream.getline(buf, sizeof(buf), 0xa);
|
||||
if (buf[0] == '#') continue;
|
||||
sscanf(buf, "FORMAT=%127s", format); // NOLINT
|
||||
sscanf(buf, "GAMMA=%f", &gamma); // NOLINT
|
||||
sscanf(buf, "EXPOSURE=%f", &exposure); // NOLINT
|
||||
if ((sscanf(buf, "%cY %u %cX %u", &sy, &height, &sx, &width) == 4)|| // NOLINT
|
||||
(sscanf(buf, "%cX %u %cY %u", &sx, &width, &sy, &height) == 4)) { // NOLINT
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
LinearImage image(width, height, 3);
|
||||
|
||||
if (sx == '-') image = horizontalFlip(image);
|
||||
if (sy == '+') image = verticalFlip(image);
|
||||
|
||||
// Allocate memory to hold one row of decoded pixel data.
|
||||
std::unique_ptr<uint8_t[]> rgbe(new uint8_t[width * 4]);
|
||||
|
||||
// First, test for non-RLE images.
|
||||
const auto pos = mStream.tellg();
|
||||
mStream.read((char*) rgbe.get(), 3);
|
||||
mStream.seekg(pos);
|
||||
|
||||
if (rgbe[0] != 0x2 || rgbe[1] != 0x2 || (rgbe[2] & 0x80) || width < 8 || width > 32767) {
|
||||
for (uint32_t y = 0; y < height; y++) {
|
||||
filament::math::float3* dst = reinterpret_cast<filament::math::float3*>(image.getPixelRef(0, y));
|
||||
mStream.read((char*) rgbe.get(), width * 4);
|
||||
// (rgb/256) * 2^(e-128)
|
||||
size_t pixel = 0;
|
||||
for (size_t x = 0; x < width; x++, pixel += 4) {
|
||||
if (rgbe[pixel + 3] == 0.0f) {
|
||||
dst[x] = filament::math::float3{0.0f};
|
||||
} else {
|
||||
filament::math::float3 v(rgbe[pixel], rgbe[pixel + 1], rgbe[pixel + 2]);
|
||||
dst[x] = (v + 0.5f) * std::ldexp(1.0f, rgbe[pixel + 3] - (128 + 8));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (uint32_t y = 0; y < height; y++) {
|
||||
uint16_t magic;
|
||||
mStream.read((char*) &magic, 2);
|
||||
if (magic != 0x0202) {
|
||||
throw std::runtime_error("invalid scanline (magic)");
|
||||
}
|
||||
|
||||
uint16_t w;
|
||||
mStream.read((char*) &w, 2);
|
||||
if (ntohs(w) != width) {
|
||||
throw std::runtime_error("invalid scanline (width)");
|
||||
}
|
||||
|
||||
char* d = (char*) rgbe.get();
|
||||
for (size_t p = 0; p < 4; p++) {
|
||||
size_t num_bytes = 0;
|
||||
while (num_bytes < width) {
|
||||
uint8_t rle_count;
|
||||
mStream.read((char*) &rle_count, 1);
|
||||
if (rle_count > 128) {
|
||||
char v;
|
||||
mStream.read(&v, 1);
|
||||
memset(d, v, size_t(rle_count - 128));
|
||||
d += rle_count - 128;
|
||||
num_bytes += rle_count - 128;
|
||||
} else {
|
||||
if (rle_count == 0) {
|
||||
throw std::runtime_error("run length is zero");
|
||||
}
|
||||
mStream.read(d, rle_count);
|
||||
d += rle_count;
|
||||
num_bytes += rle_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t const* r = &rgbe[0];
|
||||
uint8_t const* g = &rgbe[width];
|
||||
uint8_t const* b = &rgbe[2 * width];
|
||||
uint8_t const* e = &rgbe[3 * width];
|
||||
filament::math::float3* dst = reinterpret_cast<filament::math::float3*>(image.getPixelRef(0, y));
|
||||
// (rgb/256) * 2^(e-128)
|
||||
for (size_t x = 0; x < width; x++, r++, g++, b++, e++) {
|
||||
if (e[0] == 0.0f) {
|
||||
dst[x] = filament::math::float3{0.0f};
|
||||
} else {
|
||||
filament::math::float3 v(r[0], g[0], b[0]);
|
||||
dst[x] = (v + 0.5f) * std::ldexp(1.0f, e[0] - (128 + 8));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
|
||||
} catch(std::runtime_error& e) {
|
||||
// reset the stream, like we found it
|
||||
std::cerr << "Runtime error while decoding HDR: " << e.what() << std::endl;
|
||||
mStream.seekg(mStreamStartPos);
|
||||
}
|
||||
return LinearImage();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------
|
||||
|
||||
const char PSDDecoder::sig[] = { '8', 'B', 'P', 'S', 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
|
||||
|
||||
PSDDecoder* PSDDecoder::create(std::istream& stream) {
|
||||
|
||||
Reference in New Issue
Block a user