Skip to content

Commit 91e61d6

Browse files
authored
Add --{assign.convert}-texcoord-origin option to ktx create (KhronosGroup#976)
Bring toktx's --lower_left_maps_to_s0t0 and --upper_left_maps_to_s0t0 to ktx create. Includes a fix to EXR input to stop it Y flipping files with decreasing Y line order. It now always produces top-left origin images.
1 parent ba97833 commit 91e61d6

File tree

7 files changed

+349
-33
lines changed

7 files changed

+349
-33
lines changed

tests/cts

Submodule cts updated 44 files

tools/imageio/exr.imageio/exrinput.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ void ExrInput::open(ImageSpec& newspec) {
167167
width,
168168
height,
169169
1,
170+
// We make TinyEXR decode to top-left.
171+
ImageSpec::Origin(ImageSpec::Origin::eLeft, ImageSpec::Origin::eTop),
170172
header.num_channels,
171173
bitDepth,
172174
static_cast<khr_df_sample_datatype_qualifiers_e>(qualifiers),
@@ -236,6 +238,13 @@ void ExrInput::readImage(void* outputBuffer, size_t bufferByteCount,
236238
fmt::format("EXR load error: {}.", "Unsupported EXR version (2.0)"));
237239

238240
// Load image data
241+
242+
// TinyEXR decodes images so that the first bytes in the returned buffer
243+
// are the top-left corner of the image with line_order == 0 "increasing Y"
244+
// and the bottom-left corner otherwise, "decreasing Y". Force a top-left
245+
// origin regardless of the line_order in the file. See
246+
// https://github.com/syoyo/tinyexr/issues/213 for more information.
247+
header.line_order = 0;
239248
ec = LoadEXRImageFromMemory(&image, &header, exrBuffer.data(), exrBuffer.size(), &err);
240249
if (ec != TINYEXR_SUCCESS)
241250
throw std::runtime_error(fmt::format("EXR load error: {} - {}.", ec, err));
@@ -263,6 +272,9 @@ void ExrInput::readImage(void* outputBuffer, size_t bufferByteCount,
263272
channels[3] = i;
264273
else
265274
warning(fmt::format("EXR load warning: Unrecognized channel \"{}\" is ignored.", header.channels[i].name));
275+
// TODO: check for 1 channel "Y" and make greyscale texture.
276+
// TODO: check for "Y", "RY" and "BY" (luminance/chroma) and reject as unsupported.
277+
// TODO: check for "AR", "AG", "AB" and make texture with pre-multipled alpha provided there is also an A channel? Or reject?
266278
}
267279

268280
// Copy the data

tools/imageio/imageio.h

Lines changed: 83 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,18 +50,49 @@ const stride_t AutoStride = std::numeric_limits<stride_t>::min();
5050
typedef bool (*ProgressCallback)(void *opaque_data, float portion_done);
5151

5252
class ImageSpec {
53+
public:
54+
struct Origin {
55+
uint8_t x;
56+
uint8_t y;
57+
uint8_t z;
58+
59+
static const uint8_t eLeft = 0;
60+
static const uint8_t eRight = 1;
61+
static const uint8_t eTop = 0;
62+
static const uint8_t eBottom = 1;
63+
static const uint8_t eFront = 0;
64+
static const uint8_t eBack = 1;
65+
static const uint8_t eUnspecified = 0xff;
66+
67+
// This is the most common origin among image file formats, hence
68+
// the no arg constructor sets it. If unspecified, use the 3 arg ctor.
69+
Origin() : x(eLeft), y(eTop), z(eFront) { };
70+
Origin(uint8_t _x, uint8_t _y) : x(_x), y(_y), z(eFront) { }
71+
Origin(uint8_t _x, uint8_t _y, uint8_t _z) : x(_x), y(_y), z(_z) { }
72+
73+
bool unspecified() {
74+
return x == eUnspecified || y == eUnspecified || z == eUnspecified;
75+
}
76+
};
77+
5378
protected:
5479
FormatDescriptor formatDesc;
5580
uint32_t imageWidth; ///< width of the pixel data
5681
uint32_t imageHeight; ///< height of the pixel data
5782
uint32_t imageDepth; ///< depth of pixel data, >1 indicates a "volume"
83+
Origin imageOrigin; ///< logical corner of image that is the first pixel in the data stream
5884

5985
public:
60-
ImageSpec() : imageWidth(0), imageHeight(0), imageDepth(0) { }
86+
ImageSpec() : imageWidth(0), imageHeight(0),
87+
imageDepth(0), imageOrigin() { }
88+
89+
ImageSpec(uint32_t w, uint32_t h, uint32_t d, FormatDescriptor& formatDesc)
90+
: ImageSpec(w, h, d, Origin(), formatDesc) { }
6191

62-
ImageSpec(uint32_t w, uint32_t h, uint32_t d, FormatDescriptor formatDesc)
92+
ImageSpec(uint32_t w, uint32_t h, uint32_t d, Origin&& o,
93+
FormatDescriptor formatDesc)
6394
: formatDesc(std::move(formatDesc)),
64-
imageWidth(w), imageHeight(h), imageDepth(d) { }
95+
imageWidth(w), imageHeight(h), imageDepth(d), imageOrigin(o) { }
6596

6697
ImageSpec(uint32_t w, uint32_t h, uint32_t d,
6798
uint32_t channelCount, uint32_t channelBitCount,
@@ -71,8 +102,19 @@ class ImageSpec {
71102
khr_df_primaries_e p = KHR_DF_PRIMARIES_BT709,
72103
khr_df_model_e m = KHR_DF_MODEL_RGBSDA,
73104
khr_df_flags_e f = KHR_DF_FLAG_ALPHA_STRAIGHT)
105+
: ImageSpec(w, h, d, Origin(), channelCount,
106+
channelBitCount, dt, t, p, m, f) { }
107+
108+
ImageSpec(uint32_t w, uint32_t h, uint32_t d, Origin&& o,
109+
uint32_t channelCount, uint32_t channelBitCount,
110+
khr_df_sample_datatype_qualifiers_e dt
111+
= static_cast<khr_df_sample_datatype_qualifiers_e>(0),
112+
khr_df_transfer_e t = KHR_DF_TRANSFER_UNSPECIFIED,
113+
khr_df_primaries_e p = KHR_DF_PRIMARIES_BT709,
114+
khr_df_model_e m = KHR_DF_MODEL_RGBSDA,
115+
khr_df_flags_e f = KHR_DF_FLAG_ALPHA_STRAIGHT)
74116
: formatDesc(channelCount, channelBitCount, dt, t, p, m, f),
75-
imageWidth(w), imageHeight(h), imageDepth(d) { }
117+
imageWidth(w), imageHeight(h), imageDepth(d), imageOrigin(o) { }
76118

77119
ImageSpec(uint32_t w, uint32_t h, uint32_t d,
78120
uint32_t channelCount, uint32_t channelBitCount,
@@ -83,10 +125,23 @@ class ImageSpec {
83125
khr_df_primaries_e p = KHR_DF_PRIMARIES_BT709,
84126
khr_df_model_e m = KHR_DF_MODEL_RGBSDA,
85127
khr_df_flags_e f = KHR_DF_FLAG_ALPHA_STRAIGHT)
128+
: ImageSpec(w, h, d, Origin(), channelCount,
129+
channelBitCount,channelLower, channelUpper,
130+
dt, t, p, m, f) { }
131+
132+
ImageSpec(uint32_t w, uint32_t h, uint32_t d, Origin&& o,
133+
uint32_t channelCount, uint32_t channelBitCount,
134+
uint32_t channelLower, uint32_t channelUpper,
135+
khr_df_sample_datatype_qualifiers_e dt
136+
= static_cast<khr_df_sample_datatype_qualifiers_e>(0),
137+
khr_df_transfer_e t = KHR_DF_TRANSFER_UNSPECIFIED,
138+
khr_df_primaries_e p = KHR_DF_PRIMARIES_BT709,
139+
khr_df_model_e m = KHR_DF_MODEL_RGBSDA,
140+
khr_df_flags_e f = KHR_DF_FLAG_ALPHA_STRAIGHT)
86141
: formatDesc(channelCount, channelBitCount,
87142
channelLower, channelUpper,
88143
dt, t, p, m, f),
89-
imageWidth(w), imageHeight(h), imageDepth(d) { }
144+
imageWidth(w), imageHeight(h), imageDepth(d), imageOrigin(o) { }
90145

91146
ImageSpec(uint32_t w, uint32_t h, uint32_t d,
92147
uint32_t channelCount, std::vector<uint32_t>& channelBitLengths,
@@ -97,9 +152,21 @@ class ImageSpec {
97152
khr_df_primaries_e p = KHR_DF_PRIMARIES_BT709,
98153
khr_df_model_e m = KHR_DF_MODEL_RGBSDA,
99154
khr_df_flags_e f = KHR_DF_FLAG_ALPHA_STRAIGHT)
155+
: ImageSpec(w, h, d, Origin(), channelCount,
156+
channelBitLengths, channelTypes, dt, t, p, m, f) { }
157+
158+
ImageSpec(uint32_t w, uint32_t h, uint32_t d, Origin&& o,
159+
uint32_t channelCount, std::vector<uint32_t>& channelBitLengths,
160+
std::vector<khr_df_model_channels_e>& channelTypes,
161+
khr_df_sample_datatype_qualifiers_e dt
162+
= static_cast<khr_df_sample_datatype_qualifiers_e>(0),
163+
khr_df_transfer_e t = KHR_DF_TRANSFER_UNSPECIFIED,
164+
khr_df_primaries_e p = KHR_DF_PRIMARIES_BT709,
165+
khr_df_model_e m = KHR_DF_MODEL_RGBSDA,
166+
khr_df_flags_e f = KHR_DF_FLAG_ALPHA_STRAIGHT)
100167
: formatDesc(channelCount, channelBitLengths,
101168
channelTypes, dt, t, p, m, f),
102-
imageWidth(w), imageHeight(h), imageDepth(d) { }
169+
imageWidth(w), imageHeight(h), imageDepth(d), imageOrigin(o) { }
103170

104171

105172
FormatDescriptor& format() { return formatDesc; }
@@ -108,10 +175,12 @@ class ImageSpec {
108175
uint32_t width() const noexcept { return imageWidth; }
109176
uint32_t height() const noexcept { return imageHeight; }
110177
uint32_t depth() const noexcept { return imageDepth; }
178+
const Origin& origin() const noexcept { return imageOrigin; }
111179

112180
void setWidth(uint32_t w) { imageWidth = w; }
113181
void setHeight(uint32_t h) { imageHeight = h; }
114182
void setDepth(uint32_t d) { imageDepth = d; }
183+
void setOrigin(const Origin& o) { imageOrigin = o; }
115184

116185
size_t imagePixelCount() const noexcept {
117186
return depth() * width() * height();
@@ -134,6 +203,14 @@ class ImageSpec {
134203
}
135204
};
136205

206+
constexpr bool operator==(const ImageSpec::Origin& lhs, const ImageSpec::Origin& rhs) {
207+
return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z;
208+
}
209+
210+
constexpr bool operator!=(const ImageSpec::Origin& lhs, const ImageSpec::Origin& rhs) {
211+
return lhs.x != rhs.x || lhs.y != rhs.y || lhs.z != rhs.z;
212+
}
213+
137214
typedef std::function<void(const std::string&)> WarningCallbackFunction;
138215

139216
enum class ImageInputFormatType {

tools/imageio/jpg.imageio/jpginput.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ JpegInput::readHeader()
213213
// return what the decode() method will return not what is in the file.
214214

215215
images.emplace_back(ImageSpec(pJd->get_width(), pJd->get_height(), 1,
216+
ImageSpec::Origin(ImageSpec::Origin::eLeft, ImageSpec::Origin::eTop),
216217
pJd->get_num_components(), 8, // component bit length
217218
static_cast<khr_df_sample_datatype_qualifiers_e>(0),
218219
KHR_DF_TRANSFER_SRGB,

tools/imageio/npbm.imageio/npbminput.cc

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -404,12 +404,13 @@ void NpbmInput::parseGPHeader(filetype ftype)
404404
}
405405

406406
images.emplace_back(ImageSpec(width, height, 1,
407-
ftype == filetype::PPM ? 3 : 1, 8,
408-
0, maxVal,
409-
static_cast<khr_df_sample_datatype_qualifiers_e>(0),
410-
KHR_DF_TRANSFER_ITU,
411-
KHR_DF_PRIMARIES_BT709,
412-
ftype == filetype::PPM
407+
ImageSpec::Origin(ImageSpec::Origin::eLeft, ImageSpec::Origin::eTop),
408+
ftype == filetype::PPM ? 3 : 1, 8,
409+
0, maxVal,
410+
static_cast<khr_df_sample_datatype_qualifiers_e>(0),
411+
KHR_DF_TRANSFER_ITU,
412+
KHR_DF_PRIMARIES_BT709,
413+
ftype == filetype::PPM
413414
? KHR_DF_MODEL_RGBSDA
414415
: KHR_DF_MODEL_YUVSDA),
415416
ImageInputFormatType::npbm);

tools/imageio/png.imageio/pnginput.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,9 @@ PngInput::readHeader()
227227
break;
228228
}
229229

230-
images.emplace_back(ImageSpec(w, h, 1, componentCount,
230+
images.emplace_back(ImageSpec(w, h, 1,
231+
ImageSpec::Origin(ImageSpec::Origin::eLeft, ImageSpec::Origin::eTop),
232+
componentCount,
231233
bitDepth,
232234
static_cast<khr_df_sample_datatype_qualifiers_e>(0),
233235
KHR_DF_TRANSFER_UNSPECIFIED,

0 commit comments

Comments
 (0)