Skip to content

Commit e9ee2ed

Browse files
committed
oiiotool --iccread and --iccwrite
--iccread reads a named file and directly adds its contents as the "ICCProfile" metadata of the top image (as a byte array). --iccwrite takes the "ICCProfile" metadata of the top image and writes it to a named file.
1 parent a028a11 commit e9ee2ed

File tree

6 files changed

+135
-0
lines changed

6 files changed

+135
-0
lines changed

src/doc/oiiotool.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4200,6 +4200,21 @@ bottom you will see the list of all color spaces, looks, and displays that
42004200
Include/exclude subimages (see :ref:`sec-oiiotool-subimage-modifier`).
42014201

42024202

4203+
.. option:: --iccread <filename>
4204+
4205+
The `--iccread` command adds an `"ICCProfile"` attribute to the top image,
4206+
as a byte array consisting of the entire contents of the named file.
4207+
4208+
This was added to OpenImageIO 2.5.
4209+
4210+
.. option:: --iccwrite <filename>
4211+
4212+
Extract the `"ICCProfile"` attribute from the top image and writing it to
4213+
the named file.
4214+
4215+
This was added to OpenImageIO 2.5.
4216+
4217+
42034218
|
42044219
42054220
:program:`oiiotool` commands for deep images

src/oiiotool/oiiotool.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2436,6 +2436,84 @@ set_full_to_pixels(int argc, const char* argv[])
24362436

24372437

24382438

2439+
// --iccwrite (output extracted ICC profile)
2440+
static void
2441+
icc_write(cspan<const char*> argv)
2442+
{
2443+
OIIO_DASSERT(argv.size() == 2);
2444+
if (ot.postpone_callback(1, icc_write, argv))
2445+
return;
2446+
string_view command = ot.express(argv[0]);
2447+
std::string filename = ot.express(argv[1]);
2448+
OTScopedTimer timer(ot, command);
2449+
// auto options = ot.extract_options(command);
2450+
// bool allsubimages = options.get_int("allsubimages", ot.allsubimages);
2451+
2452+
ot.read();
2453+
ImageRecRef A = ot.curimg;
2454+
ImageSpec& spec(*A->spec(0));
2455+
const ParamValue* icc = spec.find_attribute("ICCProfile");
2456+
if (icc) {
2457+
bool ok = Filesystem::write_binary_file(
2458+
filename, cspan<char>((const char*)icc->data(), icc->datasize()));
2459+
if (!ok) {
2460+
ot.errorfmt(command, "Could not write ICC profile to {}", filename);
2461+
}
2462+
} else {
2463+
ot.errorfmt(command, "No ICC profile found in image.", A->name());
2464+
}
2465+
ot.num_outputs += 1;
2466+
}
2467+
2468+
2469+
2470+
// --iccread
2471+
static void
2472+
icc_read(cspan<const char*> argv)
2473+
{
2474+
OIIO_DASSERT(argv.size() == 2);
2475+
if (ot.postpone_callback(1, icc_read, argv))
2476+
return;
2477+
string_view command = ot.express(argv[0]);
2478+
std::string filename = ot.express(argv[1]);
2479+
OTScopedTimer timer(ot, command);
2480+
auto options = ot.extract_options(command);
2481+
bool allsubimages = options.get_int("allsubimages", ot.allsubimages);
2482+
2483+
if (!Filesystem::exists(filename)) {
2484+
ot.errorfmt(command, "ICC profile file {} does not exist", filename);
2485+
return;
2486+
}
2487+
size_t len = Filesystem::file_size(filename);
2488+
2489+
// Validity check: ICC profiles have a 128 byte header, and we also
2490+
// presume they are no more than 64k, so file outside that range should
2491+
// be rejected. (Is that a fair assumption?)
2492+
if (len < 1 || len >= 64 * 1024 * 1024) {
2493+
ot.errorfmt(command, "File {} is not a valid ICC profile", filename);
2494+
return;
2495+
}
2496+
2497+
std::unique_ptr<char[]> icc(new char[len]);
2498+
size_t size = Filesystem::read_bytes(filename, icc.get(), len);
2499+
if (size != len) {
2500+
ot.errorfmt(command, "Could not read ICC profile from {}", filename);
2501+
return;
2502+
}
2503+
2504+
ot.read();
2505+
ImageRecRef A = ot.curimg;
2506+
int subimages = allsubimages ? A->subimages() : 1;
2507+
for (int s = 0; s < subimages; ++s) {
2508+
(*A)(s).specmod().attribute("ICCProfile",
2509+
TypeDesc(TypeDesc::UINT8, len), icc.get());
2510+
A->update_spec_from_imagebuf(s);
2511+
}
2512+
A->metadata_modified(true);
2513+
}
2514+
2515+
2516+
24392517
// --colorconfig
24402518
static int
24412519
set_colorconfig(int argc, const char* argv[])
@@ -6596,6 +6674,12 @@ Oiiotool::getargs(int argc, char* argv[])
65966674
ap.arg("--repremult")
65976675
.help("Multiply all color channels of the current image by the alpha, but don't crush alpha=0 pixels to black.")
65986676
.action(action_repremult);
6677+
ap.arg("--iccwrite %s:FILENAME")
6678+
.help("Output the current image's ICC profile as a separate file")
6679+
.action(icc_write);
6680+
ap.arg("--iccread %s:FILENAME")
6681+
.help("Add the contents of the file to the top image as its ICC profile")
6682+
.action(icc_read);
65996683
// clang-format on
66006684

66016685
if (ap.parse_args(argc, (const char**)argv) < 0) {

testsuite/oiiotool-attribs/ref/out-jpeg9d.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,16 @@ attrib0.exr : 64 x 64, 3 channel, half openexr
109109
oiio:subimagename: "subimage01"
110110
oiio:subimages: 2
111111
openexr:chunkCount: 4
112+
Reading tahoe-icc.jpg
113+
tahoe-icc.jpg : 128 x 96, 3 channel, uint8 jpeg
114+
SHA-1: 9119399610EF6CACDAD0EBAF4D663E4EDEF70B58
115+
channel list: R, G, B
116+
ICCProfile: 0, 0, 12, 72, 76, 105, 110, 111, 2, 16, 0, 0, 109, 110, 116, 114, ... [3144 x uint8]
117+
Orientation: 1 (normal)
118+
ResolutionUnit: "in"
119+
XResolution: 72
120+
YResolution: 72
121+
Exif:ExifVersion: "0230"
122+
Exif:FlashPixVersion: "0100"
123+
jpeg:subsampling: "4:2:0"
124+
oiio:ColorSpace: "sRGB"

testsuite/oiiotool-attribs/ref/out.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,16 @@ attrib0.exr : 64 x 64, 3 channel, half openexr
109109
oiio:subimagename: "subimage01"
110110
oiio:subimages: 2
111111
openexr:chunkCount: 4
112+
Reading tahoe-icc.jpg
113+
tahoe-icc.jpg : 128 x 96, 3 channel, uint8 jpeg
114+
SHA-1: BC4C6090E793819E1A2869AAA8FBEE3587CC6947
115+
channel list: R, G, B
116+
ICCProfile: 0, 0, 12, 72, 76, 105, 110, 111, 2, 16, 0, 0, 109, 110, 116, 114, ... [3144 x uint8]
117+
Orientation: 1 (normal)
118+
ResolutionUnit: "in"
119+
XResolution: 72
120+
YResolution: 72
121+
Exif:ExifVersion: "0230"
122+
Exif:FlashPixVersion: "0100"
123+
jpeg:subsampling: "4:2:0"
124+
oiio:ColorSpace: "sRGB"
3.07 KB
Binary file not shown.

testsuite/oiiotool-attribs/run.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,13 @@
2525
command += oiiotool ("attrib2.exr " +
2626
"--eraseattrib:allsubimages=1 foo -d half -o attrib0.exr")
2727
command += info_command ("attrib0.exr", safematch=True)
28+
29+
# Test adding and extracting ICC profiles
30+
command += oiiotool ("../common/tahoe-tiny.tif --iccread ref/srgb.icc -o tahoe-icc.jpg")
31+
command += info_command ("tahoe-icc.jpg", safematch=True)
32+
command += oiiotool ("tahoe-icc.jpg --iccwrite srgb.icc")
33+
34+
outputs = [
35+
"srgb.icc",
36+
"out.txt"
37+
]

0 commit comments

Comments
 (0)