Compare commits

...
7 Commits
Author SHA1 Message Date
Larry Gritz 236d9a5801 Bump version to 2.5.0.3 2023-03-08 13:55:14 -08:00
Larry Gritz 412093fe64 fix(IC/TS): Avoid deadlock situations with file handle cache (3784)
There turns out to be a way to get wedged into a mutual deadlock on
the file handle cache, with how get_texture_info on a udim pattern
checks to see if the attribute requested is present and equally valued
in all constituent files of the udim set. This hits the handle set
pretty hard, and if multiple threads are hammering the same sets and
rapidly cycling textures out of the cache, there can be trouble, it
seems.

Remedies in this patch:

* When it's trying to close files because the number of handles is too
  high, we already had it so that if we only exceeded the max_files
  limit by a little bit, we would simply try to get the lock, and
  abandon the attempt if somebody else is holding it, but if we are
  quite a bit over, then stay and wait for the lock to be released.

  In this patch, we make the that default behavior -- if we are over
  the limit and need to free some handles, simply try to get the lock,
  and if somebody else is holding it, leave it all up to them and
  return immediately. The pros of this are (a) snappier response by
  never waiting at this point, and (b) removing the possibility of a
  deadlock, because if you can't acquire the lock, you just move
  on. But the con is that lots of threads adding new files may run
  faster than the one thread who has the file and is trying to release
  files, so it's possible that the number of handles will exceed the
  limit by a wider margin now, but hopefully only momentarily.  (But
  note that this can be overridden with a new ImageCache attribute
  "max_open_files_strict", which if set to nonzero revert to the old
  behavior. At least for now, in case I am neglecting to understand
  some bad consequence of this change.)

* The recursive_mutex controlling access to the ImageInput in each
  tile cache record is changed to a recursive_timed_mutex, and when
  closing a handle by calling release(), instead of an unconditional
  lock, try acquiring a lock for 100ms. If it fails to do so, it may
  be in a possible deadlock, so just give up and move on. The only
  consequence is that maybe that handle isn't freed after all. It will
  then move on to the next one, not the end of the world, but at least
  it can't get stuck there.

* In find_file, eliminate the call to check_max_files -- it's not
  needed here, since it's also called by open(), which is the only
  time that new file handles might be opened. We just don't also need
  it again simply because we're adding a file to the cache.
2023-03-08 11:22:48 -08:00
Fabien Castan c664dfccab rawinput: fix typo on CameraElevationAngle (#3783) 2023-03-08 11:11:15 -08:00
Larry Gritz 9bdfee17d3 api(ustringhash): make a public explicit ustringhash ctr from hash value (#3778) 2023-03-08 11:11:14 -08:00
Larry Gritz c2ff5c2734 fix: race condition in TIFF reader (#3772)
* test: Add a corrupted test file

* fix(IC): upon tile read error, mark the file as broken

The idea is to keep fruitlessly reading from a corrupted file.

* fix: race condition in TIFF reader

TIFFInput::read_native_tiles had a very subtle race condition.  There
is a section where it has a task_set of decompression tasks that are
sent to the thread queue.  When the method ends, the task_set goes out
of scope, and its destructor will wait for all the remaining tasks to
finish. All fine, right? Except that other variables in the function
include unique_ptr's with allocated memory that the tasks need.  So
what can happen is that if the compiler generates the code such that
the destructor of the unique_ptr happens before the destructor of the
task_set, the memory that the unique_ptr held may be deallocated
before some of the tasks still running in threads have finished.

In practice, we seem only to hit this race condition for particular
cases involving corrupted files, because the early `return` seems
quite likely to run those destructors before all the threads are done,
but it was possible to encounter it for valid files as well, if the
timing is just right. Shocking that we never had it reported.

The solution is just to have an explitit tasks.wait(), which will
block until all the tasks have finished before hitting the actual end
of the method and letting the other destructors to run. And for the
"early return" case, change it to a break so that we go through the
wait call at the end.

I believe that this fix addresses TALOS-2023-1709 / CVE-2023-24472.
2023-03-08 11:11:14 -08:00
angusdavisadobeandLarry Gritz 58153cd995 improve thread safety for concurrent tiff loads (#3767)
* Make lookups of const unordered_maps thread-safe

* added back deleted end brace

* clang-format

---------

Co-authored-by: Larry Gritz <lg@larrygritz.com>
2023-03-08 11:11:14 -08:00
Larry Gritz 95b7d9a263 fix: ImageOutput::check_open error conditions (#3769)
For non-0-origin images, we were checking the resolution range
incorrectly (`>=` vs `>`).

Also were printing the error message with the wrong notation, should
have shown a half-open range as `[beg,end)` and not `(beg,end]`.
2023-03-08 11:11:14 -08:00
16 changed files with 161 additions and 63 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
cmake_minimum_required (VERSION 3.12)
set (OpenImageIO_VERSION "2.5.0.2")
set (OpenImageIO_VERSION "2.5.0.3")
set (OpenImageIO_VERSION_OVERRIDE "" CACHE STRING
"Version override (use with caution)!")
mark_as_advanced (OpenImageIO_VERSION_OVERRIDE)
+10 -4
View File
@@ -98,11 +98,13 @@ public:
/// attribute/getattribute:
///
/// - `int max_open_files` :
/// The maximum number of file handles that the image cache
/// will hold open simultaneously. (Default = 100)
/// The approximate maximum number of file handles that the
/// image cache will hold open simultaneously. This is not an
/// iron-clad guarantee; the number of handles may momentarily
/// exceed this by a small percentage. (Default = 100)
/// - `float max_memory_MB` :
/// The maximum amount of memory (measured in MB) used for the
/// internal "tile cache." (Default: 256.0 MB)
/// The approximate maximum amount of memory (measured in MB)
/// used for the internal "tile cache." (Default: 256.0 MB)
/// - `string searchpath` :
/// The search path for images: a colon-separated list of
/// directories that will be searched in order for any image
@@ -190,6 +192,10 @@ public:
/// reads. The default is 1 (de-duplication turned on). The
/// only reason to set it to 0 is if you specifically want to
/// disable the de-duplication optimization.
/// - `int max_open_files_strict` :
/// If nonzero, work harder to make sure that we have
/// smaller possible overages to the max open files limit.
/// (Default: 0)
/// - `string substitute_image` :
/// When set to anything other than the empty string, the
/// ImageCache will use the named image in place of *all*
+3
View File
@@ -545,6 +545,9 @@ public:
/// How many times to retry a read failure.
/// - `int deduplicate` :
/// If nonzero, detect duplicate textures (default=1).
/// - `int max_open_files_strict` :
/// If nonzero, work harder to make sure that we have
/// smaller possible overages to the max open files limit.
/// - `string substitute_image` :
/// If supplied, an image to substatute for all texture
/// references.
+1
View File
@@ -84,6 +84,7 @@ using std::recursive_mutex;
using std::thread;
typedef std::lock_guard<mutex> lock_guard;
typedef std::lock_guard<recursive_mutex> recursive_lock_guard;
typedef std::lock_guard<std::recursive_timed_mutex> recursive_timed_lock_guard;
+8 -8
View File
@@ -837,6 +837,13 @@ public:
#endif
}
/// Construct from a raw hash value. Beware: results are undefined if it's
/// not the valid hash of a ustring.
OIIO_HOSTDEVICE explicit constexpr ustringhash(hash_t hash) noexcept
: m_hash(hash)
{
}
/// Conversion to an OIIO::string_view.
operator string_view() const noexcept { return ustring::from_hash(m_hash); }
@@ -965,7 +972,7 @@ public:
#endif
/// Return the ustringhash corresponding to the given hash. Caveat emptor:
/// results are undefined if it's not the valud hash of a ustring.
/// results are undefined if it's not the valid hash of a ustring.
OIIO_NODISCARD static constexpr ustringhash from_hash(hash_t hash)
{
ustringhash u;
@@ -977,13 +984,6 @@ private:
// Individual ustringhash internal representation -- the hash value.
rep_t m_hash;
// Construct from a raw hash value. It's protected so that it's only
// callable by its friend, ustring.
OIIO_HOSTDEVICE ustringhash(hash_t hashval)
: m_hash(hashval)
{
}
friend class ustring;
};
+49 -21
View File
@@ -105,22 +105,30 @@ struct ICCTag {
static const char*
icc_device_class_name(const std::string& device_class)
{
static std::unordered_map<std::string, const char*> device_class_names = {
{ "scnr", "Input device profile" },
{ "mntr", "Display device profile" },
{ "prtr", "Output device profile" },
{ "link", "DeviceLink profile" },
{ "spac", "ColorSpace profile" },
{ "abst", "Abstract profile" },
{ "nmcl", "NamedColor profile" },
};
return device_class_names[device_class];
static const std::unordered_map<std::string, const char*> device_class_names
= {
{ "scnr", "Input device profile" },
{ "mntr", "Display device profile" },
{ "prtr", "Output device profile" },
{ "link", "DeviceLink profile" },
{ "spac", "ColorSpace profile" },
{ "abst", "Abstract profile" },
{ "nmcl", "NamedColor profile" },
};
// std::unordered_map::operator[](const Key& key) will add the key to the map if
// it doesn't exist. This isn't what is intended and isn't thread safe.
// Instead, just do the lookup and return the value or a nullptr.
//
// return device_class_names[device_class];
auto it = device_class_names.find(device_class);
return (it != device_class_names.end()) ? it->second : nullptr;
}
static const char*
icc_color_space_name(string_view color_space)
{
static std::unordered_map<std::string, const char*> color_space_names = {
// clang-format off
static const std::unordered_map<std::string, const char*> color_space_names = {
{ "XYZ ", "XYZ" }, { "Lab ", "CIELAB" }, { "Luv ", "CIELUV" },
{ "YCbr", "YCbCr" }, { "Yxy ", "CIEYxy" }, { "RGB ", "RGB" },
{ "GRAY", "Gray" }, { "HSV ", "HSV" }, { "HLS ", "HLS" },
@@ -131,19 +139,33 @@ icc_color_space_name(string_view color_space)
{ "BCLR", "12 color" }, { "CCLR", "13 color" }, { "DCLR", "14 color" },
{ "ECLR", "15 color" }, { "FCLR", "16 color" },
};
return color_space_names[color_space];
// clang-format on
// std::unordered_map::operator[](const Key& key) will add the key to the map if
// it doesn't exist. This isn't what is intended and isn't thread safe.
// Instead, just do the lookup and return the value or a nullptr.
//
// return color_space_names[color_space];
auto it = color_space_names.find(color_space);
return (it != color_space_names.end()) ? it->second : nullptr;
}
static const char*
icc_primary_platform_name(const std::string& platform)
{
static std::unordered_map<std::string, const char*> primary_platforms = {
{ "APPL", "Apple Computer, Inc." },
{ "MSFT", "Microsoft Corporation" },
{ "SGI ", "Silicon Graphics, Inc." },
{ "SUNW", "Sun Microsystems, Inc." },
};
return primary_platforms[platform];
static const std::unordered_map<std::string, const char*> primary_platforms
= {
{ "APPL", "Apple Computer, Inc." },
{ "MSFT", "Microsoft Corporation" },
{ "SGI ", "Silicon Graphics, Inc." },
{ "SUNW", "Sun Microsystems, Inc." },
};
// std::unordered_map::operator[](const Key& key) will add the key to the map if
// it doesn't exist. This isn't what is intended and isn't thread safe.
// Instead, just do the lookup and return the value or a nullptr.
//
// return primary_platforms[platform];
auto it = primary_platforms.find(platform);
return (it != primary_platforms.end()) ? it->second : nullptr;
}
static const char*
@@ -158,7 +180,7 @@ icc_rendering_intent_name(uint32_t intent)
static std::string
icc_tag_name(const std::string& tag)
{
static std::unordered_map<std::string, std::string> tagnames = {
static const std::unordered_map<std::string, std::string> tagnames = {
{ "targ", "characterization_target" },
{ "cprt", "copyright" },
{ "desc", "profile_description" },
@@ -166,7 +188,13 @@ icc_tag_name(const std::string& tag)
{ "dmnd", "device_manufacturer_description" },
{ "vued", "viewing_conditions_description" },
};
return tagnames[tag];
// std::unordered_map::operator[](const Key& key) will add the key to the map if
// it doesn't exist. This isn't what is intended and isn't thread safe.
// Instead, just do the lookup and return the value or an empty string.
//
//return tagnames[tag];
auto it = tagnames.find(tag);
return (it != tagnames.end()) ? it->second : std::string();
}
+5 -5
View File
@@ -962,19 +962,19 @@ ImageOutput::check_open(OpenMode mode, const ImageSpec& userspec, ROI range,
m_spec.z = 0;
}
}
if (m_spec.x < range.xbegin || m_spec.x + m_spec.width >= range.xend
|| m_spec.y < range.ybegin || m_spec.y + m_spec.height >= range.yend
if (m_spec.x < range.xbegin || m_spec.x + m_spec.width > range.xend
|| m_spec.y < range.ybegin || m_spec.y + m_spec.height > range.yend
|| m_spec.z < range.zbegin
|| m_spec.z + m_spec.depth >= range.zend) {
|| m_spec.z + m_spec.depth > range.zend) {
if (m_spec.depth == 1)
errorfmt(
"{} requested pixel data window ({}, {}] x ({}, {}] exceeds the allowable range of ({}, {}] x ({}, {}]",
"{} requested pixel data window [{}, {}) x [{}, {}) exceeds the allowable range of [{}, {}) x [{}, {})",
format_name(), m_spec.x, m_spec.x + m_spec.width, m_spec.y,
m_spec.y + m_spec.height, range.xbegin, range.xend,
range.ybegin, range.yend);
else
errorfmt(
"{} requested pixel data window ({}, {}] x ({}, {}] x ({}, {}] exceeds the allowable range of ({}, {}] x ({}, {}] x ({}, {}]",
"{} requested pixel data window [{}, {}) x [{}, {}) x [{}, {}) exceeds the allowable range of [{}, {}) x [{}, {}) x [{}, {})\n{} vs {}\n",
format_name(), m_spec.x, m_spec.x + m_spec.width, m_spec.y,
m_spec.y + m_spec.height, m_spec.z, m_spec.z + m_spec.depth,
range.xbegin, range.xend, range.ybegin, range.yend,
+35 -14
View File
@@ -329,7 +329,7 @@ ImageCacheFile::get_imageinput(ImageCachePerThreadInfo* /*thread_info*/)
// Older gcc libstdc++ does not properly support std::atomic
// operations on std::shared_ptr, despite it being a C++11
// feature. No choice but to lock.
recursive_lock_guard guard(m_input_mutex);
recursive_timed_lock_guard guard(m_input_mutex);
return m_input;
#else
// True C++11: can atomically load a shared_ptr safely.
@@ -350,7 +350,7 @@ ImageCacheFile::set_imageinput(std::shared_ptr<ImageInput> newval)
// feature. No choice but to lock.
std::shared_ptr<ImageInput> oldval;
{
recursive_lock_guard guard(m_input_mutex);
recursive_timed_lock_guard guard(m_input_mutex);
oldval = m_input;
m_input = newval;
}
@@ -463,7 +463,7 @@ ImageCacheFile::open(ImageCachePerThreadInfo* thread_info)
// Now we do a lock and open it for real, to keep anybody else from
// going through the whole opening process simultaneously.
Timer input_mutex_timer;
recursive_lock_guard guard(m_input_mutex);
recursive_timed_lock_guard guard(m_input_mutex);
m_mutex_wait_time += input_mutex_timer();
// JUST IN CASE somebody else opened the file we want, between when we
@@ -840,6 +840,7 @@ ImageCacheFile::read_tile(ImageCachePerThreadInfo* thread_info, int subimage,
}
}
if (!ok) {
m_broken = true;
std::string err = inp->geterror();
if (errors_should_issue()) {
imagecache().error("{}",
@@ -1071,13 +1072,23 @@ ImageCacheFile::close()
void
ImageCacheFile::release()
{
using namespace std::chrono_literals;
Timer input_mutex_timer;
recursive_lock_guard guard(m_input_mutex);
bool locked = m_input_mutex.try_lock_for(100ms);
m_mutex_wait_time += input_mutex_timer();
if (!locked) {
// Oh boy, somebody is holding this lock. Rather than sit here even
// longer (or even worse -- deadlock), just punt and return. The worst
// that can happen is that a file that should have had its handle
// reclaimed might get held open a little longer. If there's lots of
// pressure on the cache, it'll get freed next time around.
return;
}
if (m_used)
m_used = false;
else if (m_allow_release)
close();
m_input_mutex.unlock();
}
@@ -1086,7 +1097,7 @@ void
ImageCacheFile::invalidate()
{
Timer input_mutex_timer;
recursive_lock_guard guard(m_input_mutex);
recursive_timed_lock_guard guard(m_input_mutex);
m_mutex_wait_time += input_mutex_timer();
close();
invalidate_spec();
@@ -1213,7 +1224,10 @@ ImageCacheImpl::find_file(ustring filename,
}
if (newfile) {
check_max_files(thread_info);
// We don't need to check_max_files here, because open() already
// does it, and we're only trying to limit the number of open
// files, not the number of entries in the cache.
// check_max_files(thread_info);
if (!tf->duplicate())
++thread_info->m_stats.unique_files;
}
@@ -1251,7 +1265,7 @@ ImageCacheImpl::verify_file(ImageCacheFile* tf,
if (!thread_info)
thread_info = get_perthread_info();
Timer input_mutex_timer;
recursive_lock_guard guard(tf->m_input_mutex);
recursive_timed_lock_guard guard(tf->m_input_mutex);
tf->m_mutex_wait_time += input_mutex_timer();
if (!tf->validspec()) {
tf->open(thread_info);
@@ -1355,11 +1369,13 @@ ImageCacheImpl::check_max_files(ImageCachePerThreadInfo* /*thread_info*/)
if (open_files < m_max_open_files) {
// Early out if we aren't exceeding the open file handle limit
return;
} else if (open_files < m_max_open_files + 16) {
// If we're only exceeding the open files limit by a little bit, try
// to grab the file_sweep_mutex lock. If we get it, we do the cleanup.
// But if somebody else holds it, just return rather than blocking,
// leaving the handle limit enforcement to whomever is already in this
}
if (!m_max_open_files_strict || open_files < m_max_open_files + 16) {
// If we're only exceeding the open files limit by a little bit (or by
// any amount if we aren't trying to be strict about), try to grab the
// file_sweep_mutex lock. If we get it, we do the cleanup. But if
// somebody else holds it, just return rather than blocking, leaving
// the handle limit enforcement to whomever is already in this
// function or gets here next. No need for two threads to do it at
// once or to block. If this means we may ephemerally be over the
// handle limit, so be it.
@@ -1645,6 +1661,7 @@ ImageCacheImpl::init()
m_stat_open_files_created = 0;
m_stat_open_files_current = 0;
m_stat_open_files_peak = 0;
m_max_open_files_strict = false;
// Allow environment variable to override default options
const char* options = getenv("OPENIMAGEIO_IMAGECACHE_OPTIONS");
@@ -1833,6 +1850,7 @@ ImageCacheImpl::getstats(int level) const
opt += Strutil::fmt::format("max_memory_MB={:0.1f} ",
m_max_memory_bytes / (1024.0 * 1024.0));
INTOPT(max_open_files);
BOOLOPT(max_open_files_strict);
INTOPT(autotile);
INTOPT(autoscanline);
INTOPT(automip);
@@ -2194,6 +2212,8 @@ ImageCacheImpl::attribute(string_view name, TypeDesc type, const void* val)
m_failure_retries = *(const int*)val;
} else if (name == "trust_file_extensions" && type == TypeDesc::INT) {
m_trust_file_extensions = *(const int*)val;
} else if (name == "max_open_files_strict" && type == TypeDesc::INT) {
m_max_open_files_strict = *(const int*)val;
} else if (name == "latlong_up" && type == TypeDesc::STRING) {
bool y_up = !strcmp("y", *(const char**)val);
if (y_up != m_latlong_y_up_default) {
@@ -2313,6 +2333,7 @@ ImageCacheImpl::getattribute(string_view name, TypeDesc type, void* val) const
ATTR_DECODE("deduplicate", int, m_deduplicate);
ATTR_DECODE("unassociatedalpha", int, m_unassociatedalpha);
ATTR_DECODE("trust_file_extensions", int, m_trust_file_extensions);
ATTR_DECODE("max_open_files_strict", int, m_max_open_files_strict);
ATTR_DECODE("failure_retries", int, m_failure_retries);
ATTR_DECODE("total_files", int, m_files.size());
ATTR_DECODE("max_mip_res", int, m_max_mip_res);
@@ -3399,7 +3420,7 @@ ImageCacheImpl::invalidate(ImageHandle* handle, bool force)
if (!force) {
// If not in force mode, we don't do anything if the modification
// time of the file has not changed since we opened it.
recursive_lock_guard guard(file->m_input_mutex);
recursive_timed_lock_guard guard(file->m_input_mutex);
if (file->mod_time() == Filesystem::last_write_time(file->filename())
&& !file->broken())
return;
@@ -3471,7 +3492,7 @@ ImageCacheImpl::invalidate_all(bool force)
const ImageCacheFileRef& f(fileit->second);
ustring name = f->filename();
Timer input_mutex_timer;
recursive_lock_guard guard(f->m_input_mutex);
recursive_timed_lock_guard guard(f->m_input_mutex);
f->m_mutex_wait_time += input_mutex_timer();
// If the file was broken when we opened it, or if it no longer
// exists, definitely invalidate it.
+4 -2
View File
@@ -396,8 +396,9 @@ private:
mutable int m_errors_issued; ///< Errors issued for this file
std::vector<size_t> m_mipreadcount; ///< Tile reads per mip level
ImageCacheImpl& m_imagecache; ///< Back pointer for ImageCache
mutable recursive_mutex m_input_mutex; ///< Mutex protecting the ImageInput
std::time_t m_mod_time; ///< Time file was last updated
mutable std::recursive_timed_mutex
m_input_mutex; ///< Mutex protecting the ImageInput
std::time_t m_mod_time; ///< Time file was last updated
ustring m_fingerprint; ///< Optional cryptographic fingerprint
ImageCacheFile* m_duplicate; ///< Is this a duplicate?
imagesize_t m_total_imagesize; ///< Total size, uncompressed
@@ -1173,6 +1174,7 @@ private:
bool m_unassociatedalpha; ///< Keep unassociated alpha files as they are?
bool m_latlong_y_up_default; ///< Is +y the default "up" for latlong?
bool m_trust_file_extensions = false; ///< Assume file extensions don't lie?
bool m_max_open_files_strict = false; ///< Be strict about open files limit?
int m_failure_retries; ///< Times to re-try disk failures
int m_max_mip_res = 1 << 30; ///< Don't use MIP levels higher than this
Imath::M44f m_Mw2c; ///< world-to-"common" matrix
+2 -2
View File
@@ -914,7 +914,7 @@ RawInput::open_raw(bool unpack, const std::string& name,
add("Exif", "Pressure", common.exifPressure, false, 0.0f);
add("Exif", "WaterDepth", common.exifWaterDepth, false, 0.0f);
add("Exif", "Acceleration", common.exifAcceleration, false, 0.0f);
add("Exif", "CameraElevactionAngle", common.exifCameraElevationAngle, false,
add("Exif", "CameraElevationAngle", common.exifCameraElevationAngle, false,
0.0f);
// float real_ISO;
#elif LIBRAW_VERSION >= LIBRAW_MAKE_VERSION(0, 19, 0)
@@ -931,7 +931,7 @@ RawInput::open_raw(bool unpack, const std::string& name,
add("Exif", "Pressure", other.exifPressure, false, 0.0f);
add("Exif", "WaterDepth", other.exifWaterDepth, false, 0.0f);
add("Exif", "Acceleration", other.exifAcceleration, false, 0.0f);
add("Exif", "CameraElevactionAngle", other.exifCameraElevationAngle, false,
add("Exif", "CameraElevationAngle", other.exifCameraElevationAngle, false,
0.0f);
// float real_ISO;
#endif
+6 -3
View File
@@ -2253,8 +2253,9 @@ TIFFInput::read_native_tiles(int subimage, int miplevel, int xbegin, int xend,
// xbegin, xend, ybegin, yend, zbegin, zend);
size_t tileidx = 0;
for (int z = zbegin; z < zend; z += m_spec.tile_depth) {
for (int y = ybegin; y < yend; y += m_spec.tile_height) {
for (int x = xbegin; x < xend; x += m_spec.tile_width, ++tileidx) {
for (int y = ybegin; ok && y < yend; y += m_spec.tile_height) {
for (int x = xbegin; ok && x < xend;
x += m_spec.tile_width, ++tileidx) {
char* cbuf = compressed_scratch.get() + tileidx * cbound;
char* ubuf = scratch.get() + tileidx * tile_bytes;
auto csize = TIFFReadRawTile(m_tif, tile_index(x, y, z), cbuf,
@@ -2264,7 +2265,8 @@ TIFFInput::read_native_tiles(int subimage, int miplevel, int xbegin, int xend,
errorfmt(
"TIFFReadRawTile failed reading tile x={},y={},z={}: {}",
x, y, z, err.size() ? err.c_str() : "unknown error");
return false;
ok = false;
break;
}
// Push the rest of the work onto the thread pool queue
auto out = this;
@@ -2289,6 +2291,7 @@ TIFFInput::read_native_tiles(int subimage, int miplevel, int xbegin, int xend,
}
}
}
tasks.wait();
return ok;
}
+3 -1
View File
@@ -22,6 +22,8 @@ Full command line was:
> oiiotool --oiioattrib try_all_readers 0 --info -v src/crash-1633.tif
oiiotool ERROR: read : File does not exist: "src/crash-1643.tif"
Full command line was:
> oiiotool --oiioattrib try_all_readers 0 --info -v src/crash-1643.tif
> oiiotool --oiioattrib try_all_readers 0 --info src/crash-1643.tif -o out.exr
iconvert ERROR copying "src/crash-1709.tif" to "crash-1709.exr" :
Decoding error at scanline 0, incorrect header check
Comparing "check1.tif" and "ref/check1.tif"
PASS
@@ -0,0 +1,29 @@
Reading src/separate.tif
src/separate.tif : 128 x 128, 3 channel, uint8 tiff
SHA-1: 486088DECAE711C444FDCAB009C378F7783AD9C5
channel list: R, G, B
compression: "zip"
DateTime: "2020:10:25 15:32:04"
Orientation: 1 (normal)
planarconfig: "separate"
Software: "OpenImageIO 2.3.0dev : oiiotool --pattern fill:topleft=0,0,0:topright=1,0,0:bottomleft=0,1,0:bottomright=1,1,1 128x128 3 --planarconfig separate -scanline -attrib tiff:rowsperstrip 17 -d uint8 -o separate.tif"
oiio:BitsPerSample: 8
tiff:Compression: 8
tiff:PhotometricInterpretation: 2
tiff:PlanarConfiguration: 2
tiff:RowsPerStrip: 7
Comparing "src/separate.tif" and "separate.tif"
PASS
oiiotool ERROR: read : No support for data format of "src/corrupt1.tif"
Full command line was:
> oiiotool --oiioattrib try_all_readers 0 --info -v src/corrupt1.tif
oiiotool ERROR: read : File does not exist: "src/crash-1633.tif"
Full command line was:
> oiiotool --oiioattrib try_all_readers 0 --info -v src/crash-1633.tif
oiiotool ERROR: read : File does not exist: "src/crash-1643.tif"
Full command line was:
> oiiotool --oiioattrib try_all_readers 0 --info src/crash-1643.tif -o out.exr
iconvert ERROR copying "src/crash-1709.tif" to "crash-1709.exr" :
Decoding error at scanline 0, incorrect header check
Comparing "check1.tif" and "ref/check1.tif"
PASS
+3 -1
View File
@@ -22,6 +22,8 @@ Full command line was:
> oiiotool --oiioattrib try_all_readers 0 --info -v src/crash-1633.tif
oiiotool ERROR: read : File does not exist: "src/crash-1643.tif"
Full command line was:
> oiiotool --oiioattrib try_all_readers 0 --info -v src/crash-1643.tif
> oiiotool --oiioattrib try_all_readers 0 --info src/crash-1643.tif -o out.exr
iconvert ERROR copying "src/crash-1709.tif" to "crash-1709.exr" :
Comparing "check1.tif" and "ref/check1.tif"
PASS
+2 -1
View File
@@ -17,6 +17,7 @@ command += rw_command ("src", "separate.tif")
# Test bugs we had until OIIO 2.4 for these corrupt file
command += oiiotool ("--oiioattrib try_all_readers 0 --info -v src/corrupt1.tif", failureok = True)
command += oiiotool ("--oiioattrib try_all_readers 0 --info -v src/crash-1633.tif", failureok = True)
command += oiiotool ("--oiioattrib try_all_readers 0 --info -v src/crash-1643.tif", failureok = True)
command += oiiotool ("--oiioattrib try_all_readers 0 --info src/crash-1643.tif -o out.exr", failureok = True)
command += iconvert ("src/crash-1709.tif crash-1709.exr", failureok=True)
outputs = [ "check1.tif", "out.txt" ]
Binary file not shown.