Skip to content

Commit f994436

Browse files
antond-wetalgritz
authored andcommitted
feat(raw): add thumbnail support to the raw input plugin (#4887)
This adds thumbnail support to the raw input plugin. Fixes #4107 This supports 3 of the 4 formats of thumbnails Libraw provides: BMP, JPEG, JPEGXL(untested). H265 is not supported, I don't know of any camera using it, and expect the code being significantly different from the other formats, given that the H265 is a movie container. One thing I don't like about this implementation - I understand there is no way to query the number of thumbnails available in the file? We are expected to iterate, until `get_thumbnail()` returns false? If `get_thumbnail()` returns false not because there is no thumbnail for the requested index, but any other reason, we are not going to try reading past that index. ## Tests I have tested manually with a bunch of raw images having either JPEG or BMP thumbnails. I have not tested the JPEGXL branch, as I'm not aware of any cameras using that for thumbnails. The code is identical to the JPEG branch though, so I'm not expecting any issues with that. I couldn't see any unit tests around the thumbnail functionality at all. Should I add some? I assume we can't use `oiiotool` for this? --------- Signed-off-by: Anton Dukhovnikov <[email protected]>
1 parent e28d367 commit f994436

File tree

1 file changed

+129
-1
lines changed

1 file changed

+129
-1
lines changed

src/raw.imageio/rawinput.cpp

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
#include <OpenImageIO/half.h>
1111

1212
#include <OpenImageIO/color.h>
13+
#include <OpenImageIO/filesystem.h>
1314
#include <OpenImageIO/fmath.h>
15+
#include <OpenImageIO/imagebuf.h>
1416
#include <OpenImageIO/imageio.h>
1517
#include <OpenImageIO/platform.h>
1618
#include <OpenImageIO/strutil.h>
@@ -61,7 +63,7 @@ class RawInput final : public ImageInput {
6163
const char* format_name(void) const override { return "raw"; }
6264
int supports(string_view feature) const override
6365
{
64-
return (feature == "exif"
66+
return (feature == "exif" || feature == "thumbnail"
6567
/* not yet? || feature == "iptc"*/);
6668
}
6769
bool open(const std::string& name, ImageSpec& newspec) override;
@@ -70,6 +72,7 @@ class RawInput final : public ImageInput {
7072
bool close() override;
7173
bool read_native_scanline(int subimage, int miplevel, int y, int z,
7274
void* data) override;
75+
bool get_thumbnail(ImageBuf& thumb, int subimage) override;
7376

7477
private:
7578
bool m_process = true;
@@ -1702,4 +1705,129 @@ RawInput::read_native_scanline(int subimage, int miplevel, int y, int /*z*/,
17021705
return true;
17031706
}
17041707

1708+
template<typename... Args>
1709+
void
1710+
_errorfmt(const RawInput* input, int subimage, const char* format,
1711+
const Args&... args)
1712+
{
1713+
std::string fmt = "Failed to extract thumbnail at index "
1714+
+ std::to_string(subimage) + ": " + format + ".";
1715+
input->errorfmt(fmt.c_str(), args...);
1716+
}
1717+
1718+
bool
1719+
RawInput::get_thumbnail(ImageBuf& thumb, int subimage)
1720+
{
1721+
if (m_processor == nullptr) {
1722+
_errorfmt(this, subimage,
1723+
"ImageInput hasn't been initialised properly");
1724+
return false;
1725+
}
1726+
1727+
#if LIBRAW_VERSION < LIBRAW_MAKE_VERSION(0, 21, 0)
1728+
if (subimage > 0) {
1729+
// Older versions of Libraw supported a single thumbnail per image.
1730+
// No error here.
1731+
return false;
1732+
}
1733+
int errcode = m_processor->unpack_thumb();
1734+
if (errcode != 0) {
1735+
if (errcode != LIBRAW_REQUEST_FOR_NONEXISTENT_IMAGE)
1736+
_errorfmt(this, subimage, "unpack_thumb error");
1737+
return false;
1738+
}
1739+
#else
1740+
int errcode = m_processor->unpack_thumb_ex(subimage);
1741+
if (errcode != 0) {
1742+
if (errcode != LIBRAW_REQUEST_FOR_NONEXISTENT_THUMBNAIL)
1743+
_errorfmt(this, subimage, "unpack_thumb_ex error");
1744+
return false;
1745+
}
1746+
#endif
1747+
1748+
libraw_processed_image_t* mem_thumb = m_processor->dcraw_make_mem_thumb(
1749+
&errcode);
1750+
if (mem_thumb == nullptr) {
1751+
_errorfmt(this, subimage, "dcraw_make_mem_thumb error");
1752+
return false;
1753+
}
1754+
1755+
std::string image_type;
1756+
if (mem_thumb->type == LibRaw_image_formats::LIBRAW_IMAGE_JPEG)
1757+
image_type = "jpeg";
1758+
else if (mem_thumb->type == LibRaw_image_formats::LIBRAW_IMAGE_BITMAP)
1759+
image_type = "bmp";
1760+
#if LIBRAW_VERSION >= LIBRAW_MAKE_VERSION(0, 22, 0)
1761+
else if (mem_thumb->type == LibRaw_image_formats::LIBRAW_IMAGE_JPEGXL)
1762+
image_type = "jpegxl";
1763+
else if (mem_thumb->type == LibRaw_image_formats::LIBRAW_IMAGE_H265)
1764+
image_type = "h265";
1765+
#endif
1766+
1767+
if (image_type == "h265") {
1768+
_errorfmt(this, subimage, "h265 thumbnails are not supported yet");
1769+
return false;
1770+
}
1771+
1772+
if (image_type.empty()) {
1773+
_errorfmt(this, subimage, "unknown image type {}",
1774+
static_cast<int>(mem_thumb->type));
1775+
return false;
1776+
}
1777+
1778+
if (image_type == "bmp") {
1779+
size_t data_size = mem_thumb->width * mem_thumb->height
1780+
* mem_thumb->colors;
1781+
if (data_size != mem_thumb->data_size)
1782+
return false;
1783+
1784+
ImageSpec image_spec(mem_thumb->width, mem_thumb->height,
1785+
mem_thumb->colors, TypeDesc::UCHAR);
1786+
thumb.reset(image_spec);
1787+
thumb.set_pixels(thumb.roi_full(), TypeDesc::UCHAR, mem_thumb->data);
1788+
} else {
1789+
auto image_input = OIIO::ImageInput::create(image_type, false);
1790+
if (image_input == nullptr) {
1791+
_errorfmt(this, subimage, "OIIO::ImageInput::create(\{}\") error",
1792+
image_type);
1793+
return false;
1794+
}
1795+
1796+
Filesystem::IOMemReader proxy(mem_thumb->data, mem_thumb->data_size);
1797+
bool result = image_input->valid_file(&proxy);
1798+
if (!result) {
1799+
_errorfmt(this, subimage,
1800+
"the thumbnail is not a valid image of type \"{}\"",
1801+
image_type);
1802+
return false;
1803+
}
1804+
1805+
ImageSpec temp_spec, image_spec;
1806+
Filesystem::IOProxy* pp = &proxy;
1807+
temp_spec.attribute("oiio:ioproxy", TypeDesc::PTR, &pp);
1808+
1809+
result = image_input->open("", image_spec, temp_spec);
1810+
if (!result) {
1811+
_errorfmt(
1812+
this, subimage,
1813+
"failed to initialise an ImageInput object with the thumbnail data");
1814+
return false;
1815+
}
1816+
1817+
thumb.reset(image_spec);
1818+
result = image_input->read_image(0, 0, 0, image_spec.nchannels,
1819+
image_spec.format,
1820+
thumb.localpixels());
1821+
if (!result) {
1822+
_errorfmt(
1823+
this, subimage,
1824+
"failed to initialise an ImageInput object of type \"{}\" with the thumbnail data",
1825+
image_type);
1826+
return false;
1827+
}
1828+
}
1829+
1830+
return true;
1831+
}
1832+
17051833
OIIO_PLUGIN_NAMESPACE_END

0 commit comments

Comments
 (0)